* Finished scancore-database. It now setups/repairs postgresql database config, user and scancore database.

* Fixed a bug in Tools.pm->_set_paths where a path set to '#!not_found!#' was being set (the existing value should have been left alone).
* Fixed some escaping in Log->entry. Also added some additional checks to error more gracefully.
* Created Storage->copy_file() that, well, copies files.
* Fixed Storage->find to not call Log->entry.
* Fixed Storage->make_directory() to only pass the first digit when passed a GID or UID with more that one digit (as can happen with GIDs).
* Updated Storage->write_file() to take 'secure' as a parameter to treat the file contents as containing secure data for logging purposes.
* Created System->check_daemon and ->start_daemon to check and start systemd daemons, as needed.
* Updated scancore-daemon to support running things just once on invocation (effectively run on boot or daemon restart). Call scancore-database. from here.
* Added reading striker.conf to scancore-update-states.

Signed-off-by: Digimer <digimer@alteeve.ca>
main
Digimer 8 years ago
parent 632f677f04
commit 648af130cd
  1. 35
      AN/Tools.pm
  2. 1
      AN/Tools/Get.pm
  3. 17
      AN/Tools/Log.pm
  4. 170
      AN/Tools/Storage.pm
  5. 127
      AN/Tools/System.pm
  6. 3
      AN/an-tools.xml
  7. 22
      cgi-bin/words.xml
  8. BIN
      html/skins/alteeve/images/anvil_icon_on.png
  9. BIN
      html/skins/alteeve/images/help_icon_on.png
  10. BIN
      html/skins/alteeve/images/striker_icon_on.png
  11. BIN
      html/skins/alteeve/images/users_icon_on.png
  12. 41
      tools/scancore-daemon
  13. 293
      tools/scancore-database
  14. 2
      tools/scancore-update-states

@ -570,7 +570,12 @@ sub _set_paths
# Executables
$an->data->{path} = {
configs => {
'pg_hba.conf' => "/var/lib/pgsql/data/pg_hba.conf",
'postgresql.conf' => "/var/lib/pgsql/data/postgresql.conf",
},
directories => {
backups => "/usr/sbin/striker/backups",
'cgi-bin' => "/var/www/cgi-bin",
html => "/var/www/html",
skins => "/var/www/html/skins",
@ -580,20 +585,27 @@ sub _set_paths
exe => {
'chmod' => "/usr/bin/chmod",
'chown' => "/usr/bin/chown",
cp => "/usr/bin/cp",
createdb => "/usr/bin/createdb",
createuser => "/usr/bin/createuser",
dmidecode => "/usr/sbin/dmidecode",
echo => "/usr/bin/echo",
gethostip => "/usr/bin/gethostip",
hostname => "/bin/hostname",
hostname => "/usr/bin/hostname",
ip => "/usr/sbin/ip",
logger => "/usr/bin/logger",
'mkdir' => "/usr/bin/mkdir",
psql => "/usr/bin/psql",
'postgresql-setup' => "/usr/bin/postgresql-setup",
su => "/usr/bin/su",
systemctl => "/usr/bin/systemctl",
},
secure => {
postgres_pgpass => "/var/lib/pgsql/.pgpass",
},
sysfs => {
network_interfaces => "/sys/class/net",
},
tools => {
'scancore-daemon' => "/usr/sbin/striker/scancore-daemon",
'scancore-update-states' => "/usr/sbin/striker/scancore-update-states",
},
urls => {
skins => "/skins",
},
@ -611,17 +623,8 @@ sub _set_paths
{
if (not -e $an->data->{path}{$type}{$file})
{
my $fatal = 0;
if ($type eq "words")
{
# We have to die if we don't find a words file.
$fatal = 1;
}
my $full_path = $an->Storage->find({
file => $file,
fatal => $fatal,
});
if ($full_path)
my $full_path = $an->Storage->find({file => $file});
if (($full_path) && ($full_path ne "#!not_found!#"))
{
$an->data->{path}{$type}{$file} = $full_path;
}

@ -13,7 +13,6 @@ my $THIS_FILE = "Get.pm";
### Methods;
# date_and_time
# host_uuid
# local_db_id
# network_details
# switches

@ -291,11 +291,16 @@ sub entry
}
# Clean up the string for bash
$string =~ s/"/\\\"/gs;
$string =~ s/\(/\\\(/gs;
$string =~ s/"/\\\"/gs; # Single-escape " -> \\"
$string =~ s/\\\\"/\\\\\\"/gs; # triple-escape \\" -> \\\"
#$string =~ s/\(/\\\(/gs;
# NOTE: This might become too expensive, in which case we may need to create a connection to journald
# that we can leave open during a run.
if ((not defined $tag) or (not defined $priority_string) or (not defined $an->data->{path}{exe}{logger}))
{
die $THIS_FILE." ".__LINE__."; Something not defined in Log->entry; path::exe::logger: [".$an->data->{path}{exe}{logger}."], tag: [".$tag."], 'defaults::log::tag': [".$an->data->{defaults}{'log'}{tag}."], priority_string: [".$priority_string."]\n";
}
my $shell_call = $an->data->{path}{exe}{logger}." --id --tag ".$tag." --priority ".$priority_string;
if ($server)
{
@ -393,6 +398,10 @@ sub level
$an->data->{defaults}{'log'}{level} = 4;
}
}
elsif (not $an->data->{defaults}{'log'}{level})
{
$an->data->{defaults}{'log'}{level} = 1;
}
return($an->data->{defaults}{'log'}{level});
}
@ -467,6 +476,10 @@ sub variables
my $tag = defined $parameter->{tag} ? $parameter->{tag} : $an->data->{defaults}{'log'}{tag};
# Exit immediately if this isn't going to be logged
if ((not defined $level) or (not defined $an->Log->level))
{
die $THIS_FILE." ".__LINE__."; Log->variables() called without 'level': [".$level."] or Log->level: [".$an->Log->level."] defined from: [$source : $line]\n";
}
if ($level > $an->Log->level)
{
return(1);

@ -13,6 +13,7 @@ my $THIS_FILE = "Storage.pm";
### Methods;
# change_mode
# change_owner
# copy_file
# find
# make_directory
# read_config
@ -76,7 +77,6 @@ sub parent
# Public methods #
#############################################################################################################
=head2 change_mode
This changes the mode of a file or directory.
@ -104,6 +104,10 @@ sub change_mode
my $target = defined $parameter->{target} ? $parameter->{target} : "";
my $mode = defined $parameter->{mode} ? $parameter->{mode} : "";
$an->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
target => $target,
mode => $mode,
}});
my $error = 0;
if (not $target)
@ -180,6 +184,19 @@ sub change_owner
user => $user,
}});
# Make sure the user and group and just one digit or word.
$user =~ s/^(\S+)\s.*$/$1/;
$group =~ s/^(\S+)\s.*$/$1/;
$an->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
group => $group,
user => $user,
}});
# If the group is a series of digits, remove all but the first.
if (defined $group)
{
}
my $string = "";
my $error = 0;
if (not $target)
@ -189,11 +206,11 @@ sub change_owner
$error = 1;
}
if ($user)
if (defined $user)
{
$string = $user;
}
if ($group)
if (defined $group)
{
$string .= ":".$group;
}
@ -215,6 +232,94 @@ sub change_owner
return(0);
}
=head2 copy_file
This copies a file, with a few additional checks like creating the target directory if it doesn't exist, aborting if the file has already been backed up before, etc.
# Example
$an->Storage->copy_file({source => "/some/file", target => "/another/directory/file"});
Parameters;
=head3 overwrite (optional)
If this is set to 'C<< 1 >>', and if the target file exists, it will be replaced.
If this is not passed and the target exists, this module will return 'C<< 3 >>'.
=head3 source (required)
This is the source file. if it doesn't exist, this method will return 'C<< 1 >>'.
=head3 target (required)
This is the target *B<file>*, not the directory to put it in. The target file name can be different from the source file name.
if this is not specified, 'C<< 2 >>' will be returned.
=cut
sub copy_file
{
my $self = shift;
my $parameter = shift;
my $an = $self->parent;
my $overwrite = defined $parameter->{overwrite} ? $parameter->{overwrite} : 0;
my $source = defined $parameter->{source} ? $parameter->{source} : "";
my $target = defined $parameter->{target} ? $parameter->{target} : "";
$an->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
overwrite => $overwrite,
source => $source,
target => $target,
}});
if (not $source)
{
# No source passed.
$an->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0044"});
return(1);
}
if (not $target)
{
# No target passed.
$an->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0045"});
return(2);
}
# If the target exists, abort
if ((-e $target) && (not $overwrite))
{
# This isn't an error.
$an->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "log_0046", variables => {
source => $source,
target => $target,
}});
return(3);
}
# Make sure the target directory exists and create it, if not.
my ($directory, $file) = ($target =~ /^(.*)\/(.*)$/);
$an->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
directory => $directory,
file => $file,
}});
if (not -e $directory)
{
$an->Storage->make_directory({
directory => $directory,
group => $(, # Real UID
user => $<, # Real GID
mode => "0750",
});
}
# Now backup the file.
my $output = $an->System->call({shell_call => $an->data->{path}{exe}{'cp'}." -af $source $target"});
$an->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { output => $output }});
return(0);
}
=head2 find
This searches for the given file on the system. It will search in the directories returned by C<< $an->Storage->search_directories() >>.
@ -245,8 +350,10 @@ sub find
my $parameter = shift;
my $an = $self->parent;
# Setup default values
# WARNING: Don't call Log from here! It causes it to abort
my $debug = 0;
my $file = defined $parameter->{file} ? $parameter->{file} : "";
print $THIS_FILE." ".__LINE__."; [ Debug] - file: [$file]\n" if $debug;
# Each full path and file name will be stored here before the test.
my $full_path = "#!not_found!#";
@ -255,40 +362,33 @@ sub find
foreach my $directory (@{$an->Storage->search_directories()})
{
# If "directory" is ".", expand it.
print $THIS_FILE." ".__LINE__."; [ Debug] - >> directory: [$directory]\n" if $debug;
if (($directory eq ".") && ($ENV{PWD}))
{
$directory = $ENV{PWD};
print $THIS_FILE." ".__LINE__."; [ Debug] - << directory: [$directory]\n" if $debug;
}
# Put together the initial path
my $test_path = $directory."/".$file;
print $THIS_FILE." ".__LINE__."; [ Debug] - >> test_path: [$test_path]\n" if $debug;
# Clear double-delimiters.
$test_path =~ s/\/+/\//g;
$an->Log->variables({source => $THIS_FILE, line => __LINE__, level => 1, list => { test_path => $test_path }});
print $THIS_FILE." ".__LINE__."; [ Debug] - << test_path: [$test_path]\n" if $debug;
if (-f $test_path)
{
# Found it!
$full_path = $test_path;
$an->Log->variables({source => $THIS_FILE, line => __LINE__, level => 1, list => { full_path => $full_path }});
print $THIS_FILE." ".__LINE__."; [ Debug] - >> full_path: [$full_path]\n" if $debug;
last;
}
}
# Log if we failed to find the path.
if ($full_path !~ /^\//)
{
$an->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0029", variables => { file => $file }});
}
}
else
{
# No file name passed in.
$an->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0030"});
print $THIS_FILE." ".__LINE__."; [ Debug] - << full_path: [$full_path]\n" if $debug;
}
# Return
print $THIS_FILE." ".__LINE__."; [ Debug] - full_path: [$full_path]\n" if $debug;
return ($full_path);
}
@ -336,12 +436,20 @@ sub make_directory
user => $user,
}});
# Make sure the user and group and just one digit or word.
$user =~ s/^(\S+)\s.*$/$1/;
$group =~ s/^(\S+)\s.*$/$1/;
$an->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
group => $group,
user => $user,
}});
# Break the directories apart.
my $working_directory = "";
foreach my $directory (split, /\//, $directory)
foreach my $this_directory (split/\//, $directory)
{
next if not $directory;
$working_directory .= "/$directory";
next if not $this_directory;
$working_directory .= "/$this_directory";
$working_directory =~ s/\/\//\//g;
$an->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { working_directory => $working_directory }});
if (not -e $working_directory)
@ -688,6 +796,10 @@ This is the numeric mode to set on the file. It expects four digits to cover the
Normally, if the file already exists, it won't be overwritten. Setting this to 'C<< 1 >>' will cause the file to be overwritten.
=head3 secure (optional)
If set to 'C<< 1 >>', the body is treated as containing secure data for logging purposes.
=head3 user (optional)
This is the user name or user ID to set the ownership of the file to.
@ -704,13 +816,23 @@ sub write_file
my $group = defined $parameter->{group} ? $parameter->{group} : "";
my $mode = defined $parameter->{mode} ? $parameter->{mode} : "";
my $overwrite = defined $parameter->{overwrite} ? $parameter->{overwrite} : 0;
my $secure = defined $parameter->{secure} ? $parameter->{secure} : "";
my $user = defined $parameter->{user} ? $parameter->{user} : "";
$an->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
$an->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, secure => $secure, list => {
body => $body,
file => $file,
group => $group,
mode => $mode,
overwrite => $overwrite,
secure => $secure,
user => $user,
}});
# Make sure the user and group and just one digit or word.
$user =~ s/^(\S+)\s.*$/$1/;
$group =~ s/^(\S+)\s.*$/$1/;
$an->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
group => $group,
user => $user,
}});
@ -750,8 +872,8 @@ sub write_file
# Now write the file.
my $shell_call = $file;
$an->Log->entry({source => $THIS_FILE, line => __LINE__, level => 3, key => "log_0013", variables => { shell_call => $shell_call }});
open (my $file_handle, ">", $shell_call) or $an->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0016", variables => { shell_call => $shell_call, error => $! }});
$an->Log->entry({source => $THIS_FILE, line => __LINE__, level => 3, secure => $secure, key => "log_0013", variables => { shell_call => $shell_call }});
open (my $file_handle, ">", $shell_call) or $an->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, secure => $secure, priority => "err", key => "log_0016", variables => { shell_call => $shell_call, error => $! }});
print $file_handle $body;
close $file_handle;

@ -12,6 +12,8 @@ my $THIS_FILE = "System.pm";
### Methods;
# call
# check_daemon
# start_daemon
=pod
@ -71,6 +73,24 @@ sub parent
This method makes a system call and returns the output (with the last new-line removed). If there is a problem, 'C<< #!error!# >>' is returned and the error will be logged.
Parameters;
=head3 line (optional)
This is the line number of the source file that called this method. Useful for logging and debugging.
=head3 secure (optional)
If set to 'C<< 1 >>', the shell call will be treated as if it contains a password or other sensitive data for logging.
=head3 shell_call (required)
This is the shell command to call.
=head3 source (optional)
This is the name of the source file calling this method. Useful for logging and debugging.
=cut
sub call
{
@ -78,8 +98,11 @@ sub call
my $parameter = shift;
my $an = $self->parent;
my $line = defined $parameter->{line} ? $parameter->{line} : __LINE__;
my $shell_call = defined $parameter->{shell_call} ? $parameter->{shell_call} : "";
$an->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { shell_call => $shell_call }});
my $secure = defined $parameter->{secure} ? $parameter->{secure} : 0;
my $source = defined $parameter->{source} ? $parameter->{source} : $THIS_FILE;
$an->Log->variables({source => $source, line => $line, level => 2, secure => $secure, list => { shell_call => $shell_call }});
my $output = "#!error!#";
if (not $shell_call)
@ -90,27 +113,113 @@ sub call
else
{
# Make the system call
$an->Log->entry({source => $THIS_FILE, line => __LINE__, level => 3, key => "log_0011", variables => { shell_call => $shell_call }});
open (my $file_handle, $shell_call." 2>&1 |") or $an->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0014", variables => { shell_call => $shell_call, error => $! }});
$output = "";
$an->Log->entry({source => $THIS_FILE, line => __LINE__, level => 3, secure => $secure, key => "log_0011", variables => { shell_call => $shell_call }});
open (my $file_handle, $shell_call." 2>&1 |") or $an->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, secure => $secure, priority => "err", key => "log_0014", variables => { shell_call => $shell_call, error => $! }});
while(<$file_handle>)
{
chomp;
my $line = $_;
if ($output eq "#!error!#")
{
$output = "";
}
$an->Log->entry({source => $THIS_FILE, line => __LINE__, level => 3, key => "log_0017", variables => { line => $line }});
$line =~ s/\n$//;
$line =~ s/\r$//;
$an->Log->entry({source => $THIS_FILE, line => __LINE__, level => 3, secure => $secure, key => "log_0017", variables => { line => $line }});
$output .= $line."\n";
}
close $file_handle;
chomp($output);
$output =~ s/\n$//s;
}
$an->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { output => $output }});
$an->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, secure => $secure, list => { output => $output }});
return($output);
}
=head2 check_daemon
This method checks to see if a daemon is running or not. If it is, it returns 'C<< 1 >>'. If the daemon isn't running, it returns 'C<< 0 >>'. If the daemon wasn't found, 'C<< 2 >>' is returned.
Parameters;
=head3 daemon (required)
This is the name of the daemon to check.
=cut
sub check_daemon
{
my $self = shift;
my $parameter = shift;
my $an = $self->parent;
my $return = 2;
my $daemon = defined $parameter->{daemon} ? $parameter->{daemon} : "";
$an->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { daemon => $daemon }});
my $output = $an->System->call({shell_call => $an->data->{path}{exe}{systemctl}." status ".$daemon.".service; ".$an->data->{path}{exe}{'echo'}." return_code:\$?"});
$an->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { output => $output }});
foreach my $line (split/\n/, $output)
{
if ($line =~ /return_code:(\d+)/)
{
my $return_code = $1;
if ($return_code eq "3")
{
$return = 0;
}
elsif ($return_code eq "0")
{
$return = 1;
}
}
}
$an->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { 'return' => $return }});
return($return);
}
=head2 start_daemon
This method starts a daemon.
Parameters;
=head3 daemon (required)
This is the name of the daemon to start.
=cut
sub start_daemon
{
my $self = shift;
my $parameter = shift;
my $an = $self->parent;
my $return = 2;
my $daemon = defined $parameter->{daemon} ? $parameter->{daemon} : "";
$an->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { daemon => $daemon }});
my $output = $an->System->call({shell_call => $an->data->{path}{exe}{systemctl}." start ".$daemon.".service; ".$an->data->{path}{exe}{'echo'}." return_code:\$?"});
$an->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { output => $output }});
foreach my $line (split/\n/, $output)
{
if ($line =~ /return_code:(\d+)/)
{
my $return_code = $1;
if ($return_code eq "3")
{
$return = 0;
}
elsif ($return_code eq "0")
{
$return = 1;
}
}
}
$an->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 'return' => $return }});
return($return);
}
# =head3
#

@ -63,6 +63,9 @@ It also has replacement variables: [#!variable!first!#] and [#!variable!second!#
<key name="log_0041"><![CDATA[[ Error ] - The module Storage->write_file() was asked to write the file: [#!variable!file!#] but it is not a full path. Aborting.]]></key>
<key name="log_0042"><![CDATA[[ Error ] - The module Words->string() was asked to process the string: [#!variable!string!#] which has insertion variables, but nothing was passed to the 'variables' parameter.]]></key>
<key name="log_0043"><![CDATA[[ Error ] - The module System->call() was called but 'shell_call' was not passed or was empty.]]></key>
<key name="log_0044"><![CDATA[[ Error ] - The module Storage->copy_file() was called but 'source' was not passed or was empty.]]></key>
<key name="log_0045"><![CDATA[[ Error ] - The module Storage->copy_file() was called but 'target' was not passed or was empty.]]></key>
<key name="log_0046"><![CDATA[[ Error ] - The module Storage->copy_file() was asked to copy: [#!variable!source!#] to: [#!variable!target!#], but the target already exists and 'overwrite' wasn't specified, so aborting.]]></key>
<!-- Test words. Do NOT change unless you update 't/Words.t' or tests will needlessly fail. -->
<key name="t_0000">Test</key>

@ -30,6 +30,28 @@ This is the AN::Tools master 'words' file.
<key name="js_0002">Down</key>
<key name="js_0003">Mbps</key>
<!-- Errors -->
<key name="error_0001">[ Error ] - There is a local database defined, but it does not appear to exist and we could not initialize the database server. Is 'postgresql-server' installed?</key>
<key name="error_0002">[ Error ] - Failed to start the Postgres server. Please check the system logs for details.</key>
<key name="error_0003">[ Error ] - There is no ScanCore database user set for the local machine. Please check: [#!data!path::config::striker.conf!#]'s DB entry: [#!variable!id!#].</key>
<key name="error_0004">[ Error ] - Failed to add the database user: [#!variable!user!#]! Unable to proceed.</key>
<key name="error_0005">[ Error ] - Failed to create the ScanCore database: [#!variable!database!#]</key>
<!-- Messages -->
<key name="message_0001">Initialized PostgreSQL.</key>
<key name="message_0002">Updated: [#!variable!file!#] to listen on all interfaces.</key>
<key name="message_0003">Updated: [#!variable!file!#] to require passwords for access.</key>
<key name="message_0004">Started the PostgreSQL database server.</key>
<key name="message_0005">Database user: [#!variable!user!#] already exists with ID: [#!variable!id!#].</key>
<key name="message_0006">Database user: [#!variable!user!#] was created with ID: [#!variable!id!#].</key>
<key name="message_0007">Database user: [#!variable!user!#] password has been set/updated.</key>
<key name="message_0008">ScanCore database: [#!variable!database!#] already exists.</key>
<key name="message_0009">ScanCore database: [#!variable!database!#] created.</key>
<key name="message_0010">Failed to find a local ID, no databases are stored on this machine.</key>
<!-- Warnings -->
<key name="warning_0001">[ Warning ] - Failed to delete the temporary postgres password.</key>
</language>
<!-- 日本語 -->
<language name="jp" long_name="日本語" description="Striker/ScanCore language file.">

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

@ -14,12 +14,24 @@ if (($running_directory =~ /^\./) && ($ENV{PWD}))
$running_directory =~ s/^\./$ENV{PWD}/;
}
# Turn off buffering so that the pinwheel will display while waiting for the SSH call(s) to complete.
$| = 1;
my $an = AN::Tools->new();
$an->Log->level(2);
# Turn off buffering so that the pinwheel will display while waiting for the SSH call(s) to complete.
$| = 1;
# Paths
$an->data->{path}{tools}{'scancore-database'} = "/usr/sbin/striker/scancore-database";
$an->data->{path}{tools}{'scancore-update-states'} = "/usr/sbin/striker/scancore-update-states";
$an->data->{path}{config}{'striker.conf'} = "/etc/striker/striker.conf";
# Read our config.
$an->Storage->read_config({file => $an->data->{path}{config}{'striker.conf'}});
# There are some things we only want to run on (re)start and don't need to always run.
run_once($an);
# These are the things we always want running.
while(1)
{
update_state_file($an);
@ -33,21 +45,32 @@ exit(0);
# Functions #
#############################################################################################################
# These are tools that don't need to constantly run.
sub run_once
{
my ($an) = @_;
# Check that the database is ready.
my $database_output = $an->System->call({shell_call => $an->data->{path}{tools}{'scancore-database'}, source => $THIS_FILE, line => __LINE__});
if ($database_output)
{
$an->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { database_output => $database_output }});
}
return(0);
}
# This calls 'scancore-update-states' which will scan the local machine's state (hardware and software) and
# record write it out to an HTML file
sub update_state_file
{
my ($an) = @_;
my $shell_call = $an->data->{path}{tools}{'scancore-update-states'};
$an->Log->entry({source => $THIS_FILE, line => __LINE__, level => 3, key => "log_0011", variables => { shell_call => $shell_call }});
open (my $file_handle, $shell_call." 2>&1 |") or $an->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0014", variables => { shell_call => $shell_call, error => $! }});
while(<$file_handle>)
my $states_output = $an->System->call({shell_call => $an->data->{path}{tools}{'scancore-update-states'}, source => $THIS_FILE, line => __LINE__});
if ($states_output)
{
chomp;
$an->Log->entry({source => $THIS_FILE, line => __LINE__, level => 3, key => "log_0017", variables => { output => $_ }});
$an->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { states_output => $states_output }});
}
close $file_handle;
return(0);
}

@ -1,6 +1,14 @@
#!/usr/bin/perl
#
# This checks the state of the database server and, if necessary, sets up the database.
#
# Exit codes;
# 0 = Normal exit.
# 1 = Failed to initialize postgres
# 2 = Failed to start postgres
# 3 = ScanCore user not set in the local ID in striker.conf
# 4 = Failed to create the database user.
# 5 =
use strict;
use warnings;
@ -14,22 +22,297 @@ if (($running_directory =~ /^\./) && ($ENV{PWD}))
$running_directory =~ s/^\./$ENV{PWD}/;
}
# Turn off buffering so that the pinwheel will display while waiting for the SSH call(s) to complete.
$| = 1;
my $an = AN::Tools->new();
$an->Log->level(2);
$an->Log->secure(1);
$an->Storage->read_config({file => "/etc/striker/striker.conf"});
# Paths
$an->data->{path}{tools}{'scancore-database'} = "/usr/sbin/striker/scancore-database";
$an->data->{path}{tools}{'scancore-update-states'} = "/usr/sbin/striker/scancore-update-states";
$an->data->{path}{config}{'striker.conf'} = "/etc/striker/striker.conf";
# Turn off buffering so that the pinwheel will display while waiting for the SSH call(s) to complete.
$| = 1;
$an->Storage->read_config({file => $an->data->{path}{config}{'striker.conf'}});
my $local_id = $an->Database->get_local_id;
$an->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { local_id => $local_id }});
if ($local_id)
{
print "Local ID: [$local_id]\n";
# Start checks
my $running = $an->System->check_daemon({daemon => "postgresql"});
$an->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { running => $running }});
if (not $running)
{
# Do we need to initialize the databae?
if (not -e $an->data->{path}{configs}{'pg_hba.conf'})
{
# Initialize.
my $output = $an->System->call({shell_call => $an->data->{path}{exe}{'postgresql-setup'}." initdb", source => $THIS_FILE, line => __LINE__});
$an->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { output => $output }});
# Did it succeed?
if (not -e $an->data->{path}{configs}{'pg_hba.conf'})
{
# Failed...
$an->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "error_0001"});
exit(1);
}
else
{
# Initialized!
$an->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "message_0001"});
}
# Setup postgresql.conf
my $postgresql_backup = $an->data->{path}{directories}{backups}."/pgsql/postgresql.conf";
$an->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { postgresql_backup => $postgresql_backup }});
$an->Storage->copy_file({source => $an->data->{path}{configs}{'postgresql.conf'}, target => $postgresql_backup});
my $postgresql_conf = $an->Storage->read_file({file => $an->data->{path}{configs}{'postgresql.conf'}});
my $update_file = 1;
my $new_postgresql_conf = "";
foreach my $line (split/\n/, $postgresql_conf)
{
$an->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { line => $line }});
if ($line =~ /^listen_addresses = '\*'/)
{
# No need to update.
$update_file = 0;
last;
}
elsif ($line =~ /^#listen_addresses = 'localhost'/)
{
# Inject the new listen_addresses
$new_postgresql_conf .= "listen_addresses = '*'\n";
}
$new_postgresql_conf .= $line."\n";
}
$an->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { update_file => $update_file }});
if ($update_file)
{
$an->Storage->write_file({
file => $an->data->{path}{configs}{'postgresql.conf'},
body => $new_postgresql_conf,
user => "postgres",
group => "postgres",
mode => "0600",
overwrite => 1,
});
$an->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "message_0002", variables => { file => $an->data->{path}{configs}{'postgresql.conf'} }});
}
# Setup pg_hba.conf now
my $pg_hba_backup = $an->data->{path}{directories}{backups}."/pgsql/pg_hba.conf";
$an->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { pg_hba_backup => $pg_hba_backup }});
$an->Storage->copy_file({source => $an->data->{path}{configs}{'pg_hba.conf'}, target => $pg_hba_backup});
my $pg_hba_conf = $an->Storage->read_file({file => $an->data->{path}{configs}{'pg_hba.conf'}});
$update_file = 1;
my $new_pg_hba_conf = "";
foreach my $line (split/\n/, $pg_hba_conf)
{
$an->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { line => $line }});
if ($line =~ /^host\s+all\s+all\s+\all\s+md5$/)
{
# No need to update.
$update_file = 0;
last;
}
elsif ($line =~ /^# TYPE\s+DATABASE/)
{
# Inject the new listen_addresses
$new_pg_hba_conf .= $line."\n";
$new_pg_hba_conf .= "host\tall\t\tall\t\t*\t\t\tmd5\n";
}
else
{
$new_pg_hba_conf .= $line."\n";
}
}
$an->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { update_file => $update_file }});
if ($update_file)
{
$an->Storage->write_file({
file => $an->data->{path}{configs}{'pg_hba.conf'},
body => $new_pg_hba_conf,
user => "postgres",
group => "postgres",
mode => "0600",
overwrite => 1,
});
$an->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "message_0003", variables => { file => $an->data->{path}{configs}{'postgresql.conf'} }});
}
}
# Start the daemon. It might fail if it has never been initialized.
my $started = $an->System->start_daemon({daemon => "postgresql"});
$an->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { started => $started }});
if ($started)
{
# Started the daemon.
$an->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "message_0004"});
}
else
{
# Failed to start
$an->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "error_0002"});
exit(2);
}
}
# Create the .pgpass file, if needed.
my $created_pgpass = 0;
$an->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, secure => 1, list => {
'path::secure::postgres_pgpass' => $an->data->{path}{secure}{postgres_pgpass},
"database::${local_id}::password" => $an->data->{database}{$local_id}{password},
}});
if ((not -e $an->data->{path}{secure}{postgres_pgpass}) && ($an->data->{database}{$local_id}{password}))
{
my $body = "*:*:*:postgres:".$an->data->{database}{$local_id}{password}."\n";
$an->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, secure => 1, list => { body => $body }});
$an->Storage->write_file({
file => $an->data->{path}{secure}{postgres_pgpass},
body => $body,
user => "postgres",
group => "postgres",
mode => "0600",
overwrite => 1,
secure => 1,
});
if (-e $an->data->{path}{secure}{postgres_pgpass})
{
$created_pgpass = 1;
$an->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { created_pgpass => $created_pgpass }});
}
}
# Does the database user exist?
my $create_user = 1;
my $scancore_user = $an->data->{database}{$local_id}{user};
$an->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { scancore_user => $scancore_user }});
if (not $scancore_user)
{
# No database user defined
$an->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "error_0003", variables => { id => $local_id }});
exit(3);
}
my $user_list = $an->System->call({shell_call => $an->data->{path}{exe}{su}." - postgres -c \"".$an->data->{path}{exe}{psql}." template1 -c 'SELECT usename, usesysid FROM pg_catalog.pg_user;'\"", source => $THIS_FILE, line => __LINE__});
$an->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { user_list => $user_list }});
foreach my $line (split/\n/, $user_list)
{
if ($line =~ /^ $scancore_user\s+\|\s+(\d+)/)
{
# User exists already
my $id = $1;
$an->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "message_0005", variables => { user => $scancore_user, id => $id }});
$create_user = 0;
last;
}
}
$an->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { create_user => $create_user }});
if ($create_user)
{
# Create the user
my $create_output = $an->System->call({shell_call => $an->data->{path}{exe}{su}." - postgres -c \"".$an->data->{path}{exe}{createuser}." --no-superuser --createdb --no-createrole $scancore_user\"", source => $THIS_FILE, line => __LINE__});
my $user_list = $an->System->call({shell_call => $an->data->{path}{exe}{su}." - postgres -c \"".$an->data->{path}{exe}{psql}." template1 -c 'SELECT usename, usesysid FROM pg_catalog.pg_user;'\"", source => $THIS_FILE, line => __LINE__});
my $user_exists = 0;
$an->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { create_output => $create_output, user_list => $user_list }});
foreach my $line (split/\n/, $user_list)
{
if ($line =~ /^ $scancore_user\s+\|\s+(\d+)/)
{
# Success!
my $id = $1;
$an->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "message_0006", variables => { user => $scancore_user, id => $id }});
$user_exists = 1;
last;
}
}
if (not $user_exists)
{
$an->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "error_0004", variables => { user => $scancore_user }});
exit(4);
}
# Update/set the passwords.
if ($an->data->{database}{$local_id}{password})
{
foreach my $user ("postgres", $scancore_user)
{
my $update_output = $an->System->call({secure => 1, shell_call => $an->data->{path}{exe}{su}." - postgres -c \"".$an->data->{path}{exe}{psql}." template1 -c \\\"ALTER ROLE $user WITH PASSWORD '".$an->data->{database}{$local_id}{password}."';\\\"\"", source => $THIS_FILE, line => __LINE__});
$an->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, secure => 1, list => { update_output => $update_output }});
foreach my $line (split/\n/, $user_list)
{
if ($line =~ /ALTER ROLE/)
{
# Password set
$an->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "message_0007", variables => { user => $user }});
}
}
}
}
}
# Create the database, if needed.
my $create_database = 1;
my $scancore_database = $an->data->{database}{$local_id}{name};
$an->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "database::${local_id}::name" => $an->data->{database}{$local_id}{name} }});
my $database_list = $an->System->call({shell_call => $an->data->{path}{exe}{su}." - postgres -c \"".$an->data->{path}{exe}{psql}." template1 -c 'SELECT datname FROM pg_catalog.pg_database;'\"", source => $THIS_FILE, line => __LINE__});
$an->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { database_list => $database_list }});
foreach my $line (split/\n/, $database_list)
{
if ($line =~ /^ $scancore_database$/)
{
# Database already exists.
$an->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "message_0008", variables => { database => $scancore_database }});
$create_database = 0;
last;
}
}
$an->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { create_database => $create_database }});
if ($create_database)
{
my $create_output = $an->System->call({shell_call => $an->data->{path}{exe}{su}." - postgres -c \"".$an->data->{path}{exe}{createdb}." --owner $scancore_user $scancore_database\"", source => $THIS_FILE, line => __LINE__});
$an->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { create_output => $create_output }});
my $database_exists = 0;
my $database_list = $an->System->call({shell_call => $an->data->{path}{exe}{su}." - postgres -c \"".$an->data->{path}{exe}{psql}." template1 -c 'SELECT datname FROM pg_catalog.pg_database;'\"", source => $THIS_FILE, line => __LINE__});
$an->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { database_list => $database_list }});
foreach my $line (split/\n/, $database_list)
{
if ($line =~ /^ $scancore_database$/)
{
# Database created
$an->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "message_0008", variables => { database => $scancore_database }});
$database_exists = 1;
last;
}
}
if (not $database_exists)
{
$an->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "error_0005", variables => { database => $scancore_database }});
exit(5);
}
}
# Remove the temporary password file.
if (($created_pgpass) && (-e $an->data->{path}{secure}{postgres_pgpass}))
{
unlink $an->data->{path}{secure}{postgres_pgpass};
if (-e $an->data->{path}{secure}{postgres_pgpass})
{
# Failed to unlink the file.
$an->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "alert", key => "warning_0001"});
}
}
}
else
{
print "Failed to find a local ID, no databases are stored on this machine.\n";
# Didn't find an entry for this machine.
$an->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "message_0010"});
}
exit(0);

@ -17,6 +17,8 @@ if (($running_directory =~ /^\./) && ($ENV{PWD}))
my $an = AN::Tools->new();
$an->Log->level(2);
$an->Storage->read_config({file => "/etc/striker/striker.conf"});
# Turn off buffering so that the pinwheel will display while waiting for the SSH call(s) to complete.
$| = 1;

Loading…
Cancel
Save