diff --git a/cualsea-server/Makefile.PL b/cualsea-server/Makefile.PL index 0716c47..11cd468 100644 --- a/cualsea-server/Makefile.PL +++ b/cualsea-server/Makefile.PL @@ -10,5 +10,11 @@ WriteMakefile( PREREQ_PM => { 'DBD::SQLite' => 0, 'DateTime' => 0, + 'Email::Sender::Simple' => 0, + 'Email::Sender::Transport::SMTP' => 0, + 'Email::MIME' => 0, + 'IO::Socket::SSL' => 0, + 'MIME::Base64' => 0, + 'Authen::SASL' => 0, } ); diff --git a/cualsea-server/bin/cualsead b/cualsea-server/bin/cualsead index 7a3aaae..56296e1 100755 --- a/cualsea-server/bin/cualsead +++ b/cualsea-server/bin/cualsead @@ -6,15 +6,34 @@ use strict; use warnings; use Carp qw/confess/; +use POSIX qw/WNOHANG/; + +use Try::Tiny; use Cualsea::Server::Loop; +use Cualsea::Server::MonitorController; -my $loop = Cualsea::Server::Loop->new; +my $pid = fork; +if ($pid) {{ + local $SIG{INT} = \&finish_parent; + while (1) { + my $loop = Cualsea::Server::Loop->new; + try { + $loop->run; + } catch { + confess $_; + }; + } + finish_parent(); +}} +my $monitor_controller = Cualsea::Server::MonitorController->new; while (1) { - try { - $loop->run; - } catch { - confess $_; - }; + $monitor_controller->loop; +} + +sub finish_parent { + kill 'INT', $pid; + 1 while waitpid $pid, WNOHANG; + exit; } diff --git a/cualsea-server/lib/Cualsea/Server/DB.pm b/cualsea-server/lib/Cualsea/Server/DB.pm index 0c8d61a..361e406 100644 --- a/cualsea-server/lib/Cualsea/Server/DB.pm +++ b/cualsea-server/lib/Cualsea/Server/DB.pm @@ -25,6 +25,11 @@ my @migrations = ( date_execution TEXT, parameters TEXT, result TEXT + );', + 'CREATE TABLE monitor ( + id INTEGER PRIMARY KEY, + date_execution TEXT, + is_up INTEGER );' ); diff --git a/cualsea-server/lib/Cualsea/Server/Mail.pm b/cualsea-server/lib/Cualsea/Server/Mail.pm new file mode 100644 index 0000000..6333e77 --- /dev/null +++ b/cualsea-server/lib/Cualsea/Server/Mail.pm @@ -0,0 +1,88 @@ +package Cualsea::Server::Mail; + +use v5.30.0; + +use strict; +use warnings; + +use Const::Fast; +use Email::Sender::Transport::SMTP; +use Email::Sender::Simple qw/sendmail/; +use Email::MIME; + +const my $MAIL_CONFIG_FILE => '/etc/cualsea/mail.conf'; +const my $REPORT_TO_CONFIG_FILE => '/etc/cualsea/report_to'; +const my $FROM_CONFIG_FILE => '/etc/cualsea/from'; + +sub new { + my $class = shift; + return bless {}, $class; +} + +sub notify_service_down { + my $self = shift; + my $name = shift; + + my $mime = Email::MIME->create( + header_str => [ + From => $self->from, + To => [ $self->report_to ], + Subject => "Service $name down", + ], + parts => [ + Email::MIME->create( + attributes => { + content_type => 'text/plain', + disposition => 'attachment', + charset => 'UTF-8', + encoding => "8bit", + }, + body_str => "$name is not up", + ) + ] + ); + sendmail($mime, { transport => $self->transport }); +} + +sub report_to { + my $self = shift; + if (!defined $self->{report_to}) { + open my $fh, '<', $REPORT_TO_CONFIG_FILE or die "Unable to get mail to report from config file"; + my $email = <$fh>; + chomp $email; + close $fh; + $self->{report_to} = $email; + } + return $self->{report_to}; +} + +sub from { + my $self = shift; + if (!defined $self->{from}) { + open my $fh, '<', $FROM_CONFIG_FILE or die "Unable to get mail From from config file"; + my $email = <$fh>; + chomp $email; + close $fh; + $self->{from} = $email; + } + return $self->{from}; +} + +sub transport { + my $self = shift; + if (!defined $self->{transport}) { + my %parameters; + open my $fh, '<', $MAIL_CONFIG_FILE or die "Unable to get transport from config file"; + while (my $line = <$fh>) { + my ($key, $value) = $line =~ /^(.*?)=(.*?)\s*$/; + if ($key eq 'hosts') { + $value = [ $value ]; + } + $parameters{$key} = $value; + } + close $fh; + $self->{transport} = Email::Sender::Transport::SMTP->new(%parameters); + } + return $self->{transport}; +} +1; diff --git a/cualsea-server/lib/Cualsea/Server/MessageController.pm b/cualsea-server/lib/Cualsea/Server/MessageController.pm index b05f054..2624643 100644 --- a/cualsea-server/lib/Cualsea/Server/MessageController.pm +++ b/cualsea-server/lib/Cualsea/Server/MessageController.pm @@ -18,6 +18,7 @@ use JSON; use Cualsea::MessageManager; use Cualsea::Server::DB; +use Cualsea::Server::Service; sub handle_add { my %params = @_; @@ -62,7 +63,7 @@ sub handle_del { my $name = $params{name}; my $dbh = Cualsea::Server::DB->dbh(); - if ( !check_if_service_exists_in_db($name) ) { + if ( !Cualsea::Server::Service::check_if_service_exists_in_db($name) ) { return { is_error => 1, desc => 'Service not found.', @@ -81,23 +82,13 @@ EOF }; } -sub check_if_service_exists_in_db { - my $name = shift; - my $dbh = Cualsea::Server::DB->dbh(); - my $service = $dbh->selectrow_hashref( <<"EOF", undef, $name ); -SELECT name FROM services WHERE name = ? -EOF - if ( !defined $service ) { - return 0; - } - return 1; -} + sub handle_start { my %params = @_; my $name = $params{name}; - if ( !check_if_service_exists_in_db($name) ) { + if ( !Cualsea::Server::Service::check_if_service_exists_in_db($name) ) { return { is_error => 1, desc => 'Service not found.', @@ -105,7 +96,7 @@ sub handle_start { }; } - if ( _is_started_service($name) ) { + if ( Cualsea::Server::Service::is_started_service($name) ) { return { is_error => 1, desc => 'Service already started.', @@ -120,23 +111,19 @@ sub handle_start { }; } -sub _is_started_service { - my $name = shift; - return !system "/etc/init.d/$name", 'status'; -} sub handle_stop { my %params = @_; my $name = $params{name}; - if ( !check_if_service_exists_in_db($name) ) { + if ( !Cualsea::Server::Service::check_if_service_exists_in_db($name) ) { return { is_error => 1, desc => 'Service not found.', status => 404, }; } - if ( !_is_started_service($name) ) { + if ( !Cualsea::Server::Service::is_started_service($name) ) { return { is_error => 1, desc => 'Service already stopped.', @@ -156,7 +143,7 @@ sub handle_restart { my %params = @_; my $name = $params{name}; - if ( !check_if_service_exists_in_db($name) ) { + if ( !Cualsea::Server::Service::check_if_service_exists_in_db($name) ) { return { is_error => 1, desc => 'Service not found.', diff --git a/cualsea-server/lib/Cualsea/Server/MonitorController.pm b/cualsea-server/lib/Cualsea/Server/MonitorController.pm new file mode 100644 index 0000000..d448ba6 --- /dev/null +++ b/cualsea-server/lib/Cualsea/Server/MonitorController.pm @@ -0,0 +1,68 @@ +package Cualsea::Server::MonitorController; + +use v5.30.0; + +use strict; +use warnings; + +use DateTime; + +use Cualsea::Server::DB; +use Cualsea::Server::Service; +use Cualsea::Server::Mail; + +sub new { + my $class = shift; + return bless {}, $class; +} + +sub last_date { + my $self = shift; + if ( !exists $self->{last_date} ) { + $self->{last_date} = DateTime->now(); + } + return $self->{last_date}; +} + +sub loop { + my $self = shift; + my $last_date = $self->last_date; + my $current_date = DateTime->now(); + if ( $current_date > $last_date->clone->add( minutes => 1 ) ) { + $self->{last_date} = $current_date; + $self->monitor; + } +} + +sub monitor { + my $self = shift; + my $dbh = Cualsea::Server::DB->dbh(); + my $services = $dbh->selectall_arrayref( <<'EOF', { Slice => {} } ); +SELECT name,pidfile FROM services; +EOF + for my $service (@$services) { + my $pidfile = $service->{pidfile}; + my $name = $service->{name}; + my $started = Cualsea::Server::Service::is_started_service($name); + if ($started) {{ + my $success_opening = open my $fh, '<', $pidfile; + if (!$success_opening) { + $started = 0; + # Cool trick to escape if {{ }} + next; + } + my $pid = <$fh>; + chomp $pid; + $started = !system 'ps', '-p', $pid; + close $fh; + }} + $dbh->do( 'INSERT INTO monitor (date_execution, is_up) VALUES (?, ?);', + undef, DateTime->now() . '', $started ); + if (!$started) { + say "$name stopped"; + my $mailer = Cualsea::Server::Mail->new(); + $mailer->notify_service_down($name); + } + } +} +1; diff --git a/cualsea-server/lib/Cualsea/Server/Service.pm b/cualsea-server/lib/Cualsea/Server/Service.pm new file mode 100644 index 0000000..02e8ffc --- /dev/null +++ b/cualsea-server/lib/Cualsea/Server/Service.pm @@ -0,0 +1,24 @@ +package Cualsea::Server::Service; + +use v5.30.0; + +use strict; +use warnings; + +sub is_started_service { + my $name = shift; + return !system "/etc/init.d/$name", 'status'; +} + +sub check_if_service_exists_in_db { + my $name = shift; + my $dbh = Cualsea::Server::DB->dbh(); + my $service = $dbh->selectrow_hashref( <<"EOF", undef, $name ); +SELECT name FROM services WHERE name = ? +EOF + if ( !defined $service ) { + return 0; + } + return 1; +} +1;