#!/usr/bin/perl # # This checks the state of the postgresql database server and, if necessary, configures it for external # access, initializes it and gets it running. # # 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; use Data::Dumper; use AN::Tools; my $THIS_FILE = ($0 =~ /^.*\/(.*)$/)[0]; my $running_directory = ($0 =~ /^(.*?)\/$THIS_FILE$/)[0]; 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({set => 2}); $an->Log->secure({set => 1}); # Paths $an->data->{path}{tools}{'an-prep-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"; $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) { # 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 { # Didn't find an entry for this machine. $an->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "message_0010"}); } exit(0); ############################################################################################################# # Functions # #############################################################################################################