405 lines
9.8 KiB
Perl
405 lines
9.8 KiB
Perl
package Cualsea::Server::MessageController;
|
|
|
|
use v5.30.0;
|
|
|
|
use strict;
|
|
use warnings;
|
|
|
|
use Data::Dumper;
|
|
use Socket qw/SOL_SOCKET SO_PEERCRED/;
|
|
use List::Util qw/any/;
|
|
use Carp qw/carp/;
|
|
|
|
use Params::ValidationCompiler qw/validation_for/;
|
|
use Types::Standard qw/Object HashRef Str/;
|
|
use Try::Tiny;
|
|
|
|
use Cualsea::MessageManager;
|
|
use Cualsea::Server::DB;
|
|
|
|
sub handle_add {
|
|
my %params = @_;
|
|
my $name = $params{name};
|
|
my $init = $params{init};
|
|
my $pidfile = $params{pidfile};
|
|
my $binpath = $params{binpath};
|
|
if ( !-e $binpath || $init ne 'sysvinit' || !_validate_init_name($name) ) {
|
|
return {
|
|
is_error => 1,
|
|
desc => "Parameters didn't pass contraints on add",
|
|
status => 400
|
|
};
|
|
}
|
|
my $dbh = Cualsea::Server::DB->dbh();
|
|
|
|
eval {
|
|
$dbh->do( << 'EOF', undef, $name, $init, $pidfile, $binpath );
|
|
INSERT INTO services (name, init, pidfile, binpath) VALUES (?, ?, ?, ?);
|
|
EOF
|
|
};
|
|
if ($@) {
|
|
if ( $@ =~ /UNIQUE/i ) {
|
|
my $return_value = {
|
|
status => 400,
|
|
is_error => 1,
|
|
desc => 'Endpoint already exists.',
|
|
};
|
|
return $return_value;
|
|
}
|
|
carp $@;
|
|
}
|
|
return {
|
|
is_error => 0,
|
|
desc => "Succesful creation of init service $name.",
|
|
status => 200
|
|
};
|
|
}
|
|
|
|
sub handle_del {
|
|
my %params = @_;
|
|
my $name = $params{name};
|
|
my $dbh = Cualsea::Server::DB->dbh();
|
|
|
|
if ( !check_if_service_exists_in_db($name) ) {
|
|
return {
|
|
is_error => 1,
|
|
desc => 'Service not found.',
|
|
status => 404,
|
|
};
|
|
}
|
|
|
|
$dbh->do( <<'EOF', undef, $name );
|
|
DELETE FROM services WHERE name = ?
|
|
EOF
|
|
|
|
return {
|
|
is_error => 0,
|
|
desc => "Succesful deletion of init service $name.",
|
|
status => 200,
|
|
};
|
|
}
|
|
|
|
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) ) {
|
|
return {
|
|
is_error => 1,
|
|
desc => 'Service not found.',
|
|
status => 404,
|
|
};
|
|
}
|
|
|
|
if ( _is_started_service($name) ) {
|
|
return {
|
|
is_error => 1,
|
|
desc => 'Service already started.',
|
|
status => 400,
|
|
};
|
|
}
|
|
system 'sudo', "/etc/init.d/$name", 'start';
|
|
return {
|
|
is_error => 0,
|
|
desc => 'Service started.',
|
|
status => 200,
|
|
};
|
|
}
|
|
|
|
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) ) {
|
|
return {
|
|
is_error => 1,
|
|
desc => 'Service not found.',
|
|
status => 404,
|
|
};
|
|
}
|
|
if ( !_is_started_service($name) ) {
|
|
return {
|
|
is_error => 1,
|
|
desc => 'Service already stopped.',
|
|
status => 400,
|
|
};
|
|
}
|
|
system 'sudo', "/etc/init.d/$name", 'stop';
|
|
return {
|
|
is_error => 0,
|
|
desc => 'Service stopped.',
|
|
status => 200,
|
|
};
|
|
|
|
}
|
|
|
|
sub handle_restart {
|
|
my %params = @_;
|
|
my $name = $params{name};
|
|
|
|
if ( !check_if_service_exists_in_db($name) ) {
|
|
return {
|
|
is_error => 1,
|
|
desc => 'Service not found.',
|
|
status => 404,
|
|
};
|
|
|
|
}
|
|
system 'sudo', "/etc/init.d/$name", 'restart';
|
|
|
|
return {
|
|
is_error => 0,
|
|
desc => 'Service restarted.',
|
|
status => 200,
|
|
};
|
|
}
|
|
|
|
my %COMMANDS = (
|
|
add => {
|
|
params => [
|
|
'name',
|
|
'init', # Only sysvinit supported
|
|
'pidfile',
|
|
'binpath'
|
|
],
|
|
validator => validation_for(
|
|
params => {
|
|
name => { type => Str },
|
|
init => { type => Str },
|
|
pidfile => { type => Str },
|
|
binpath => { type => Str },
|
|
}
|
|
),
|
|
handle => \&handle_add,
|
|
},
|
|
del => {
|
|
params => ['name'],
|
|
validator => validation_for(
|
|
params => {
|
|
name => { type => Str },
|
|
}
|
|
),
|
|
handle => \&handle_del,
|
|
},
|
|
report => {
|
|
params => [],
|
|
validator => sub { },
|
|
},
|
|
start => {
|
|
params => ['name'],
|
|
validator => validation_for(
|
|
params => {
|
|
name => { type => Str },
|
|
}
|
|
),
|
|
handle => \&handle_start,
|
|
},
|
|
stop => {
|
|
params => ['name'],
|
|
validator => validation_for(
|
|
params => {
|
|
name => { type => Str },
|
|
}
|
|
),
|
|
handle => \&handle_stop,
|
|
},
|
|
restart => {
|
|
params => ['name'],
|
|
validator => validation_for(
|
|
params => {
|
|
name => { type => Str },
|
|
}
|
|
),
|
|
handle => \&handle_restart,
|
|
}
|
|
);
|
|
|
|
sub _validate_init_name {
|
|
my $init_name = shift;
|
|
if ( $init_name =~ /^\.$/ ) {
|
|
return 0;
|
|
}
|
|
if ( $init_name =~ /^\.\.$/ ) {
|
|
return 0;
|
|
}
|
|
if ( $init_name =~ /\// ) {
|
|
return 0;
|
|
}
|
|
if ( !-e "/etc/init.d/$init_name" ) {
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
sub new {
|
|
my $class = shift;
|
|
return bless {}, $class;
|
|
}
|
|
|
|
{
|
|
my $validator = validation_for(
|
|
params => {
|
|
socket => { type => Object },
|
|
}
|
|
);
|
|
|
|
sub dispatch {
|
|
my $self = shift;
|
|
my %params = $validator->(@_);
|
|
my $socket = $params{socket};
|
|
my $message_manager = Cualsea::MessageManager->new( socket => $socket );
|
|
my $message = $message_manager->read_message;
|
|
if ( !$self->check_if_user_has_permissions( socket => $socket ) ) {
|
|
$self->write_no_permission( message_manager => $message_manager );
|
|
return;
|
|
}
|
|
if ( !$self->check_is_command( message => $message ) ) {
|
|
$self->write_malformed( message_manager => $message_manager );
|
|
return;
|
|
}
|
|
$message_manager->write_message(
|
|
message => $self->_handle_command( message => $message ) );
|
|
}
|
|
}
|
|
|
|
{
|
|
my $validator = validation_for(
|
|
params => {
|
|
message => { type => HashRef },
|
|
}
|
|
);
|
|
|
|
sub _handle_command {
|
|
my $self = shift;
|
|
my %params = $validator->(@_);
|
|
my $message = $params{message};
|
|
my $command = $message->{command};
|
|
my $params = $message->{arguments} // [];
|
|
say "Got parameters:";
|
|
say Data::Dumper::Dumper $params;
|
|
if ( !exists $COMMANDS{$command} ) {
|
|
return { is_error => 1, status => 404 };
|
|
}
|
|
|
|
my $command_info = $COMMANDS{$command};
|
|
|
|
my $params_info = $command_info->{params};
|
|
print Data::Dumper::Dumper $params_info;
|
|
|
|
# We want to forget about $params arrayref
|
|
# and previous hash as soon as possible.
|
|
%params = ();
|
|
for my $param_info_index (
|
|
scalar @$params_info ? ( 0 .. $#$params_info ) : () )
|
|
{
|
|
|
|
my $param_info = $params_info->[$param_info_index];
|
|
|
|
# May not come defined,
|
|
if ( $param_info_index > scalar $#$params ) {
|
|
last;
|
|
}
|
|
say "$param_info => @{[$params->[$param_info_index]]}";
|
|
$params{$param_info} = $params->[$param_info_index];
|
|
}
|
|
my $validator = $command_info->{validator};
|
|
my $return_error;
|
|
try {
|
|
$validator->(%params);
|
|
}
|
|
catch {
|
|
$return_error = { is_error => 1, status => 400, desc => $_->error };
|
|
};
|
|
if ( defined $return_error ) {
|
|
return $return_error;
|
|
}
|
|
|
|
my $handle = $command_info->{handle};
|
|
if ( !defined $handle ) {
|
|
return { is_error => 1, status => 501 };
|
|
}
|
|
return $handle->(%params);
|
|
}
|
|
}
|
|
|
|
{
|
|
my $validator = validation_for(
|
|
params => {
|
|
socket => { type => Object },
|
|
}
|
|
);
|
|
|
|
sub check_if_user_has_permissions {
|
|
my $self = shift;
|
|
my %params = $validator->(@_);
|
|
my $socket = $params{socket};
|
|
my ( $pid, $uid, $gid ) = unpack 'LLL', $socket->sockopt(SO_PEERCRED);
|
|
my $user = getpwuid($uid);
|
|
my ( undef, undef, undef, $members ) = getgrnam('cualsea');
|
|
return any { $_ eq $user } split ' ', $members;
|
|
}
|
|
}
|
|
{
|
|
my $validator = validation_for(
|
|
params => {
|
|
message_manager => { type => Object }
|
|
}
|
|
);
|
|
|
|
sub write_no_permission {
|
|
my $self = shift;
|
|
my %params = $validator->(@_);
|
|
my $message_manager = $params{message_manager};
|
|
$message_manager->write_message(
|
|
message => { is_error => 1, status => 403 } );
|
|
}
|
|
}
|
|
|
|
{
|
|
my $validator = validation_for(
|
|
params => {
|
|
message_manager => { type => Object }
|
|
}
|
|
);
|
|
|
|
sub write_malformed {
|
|
my $self = shift;
|
|
my %params = $validator->(@_);
|
|
my $message_manager = $params{message_manager};
|
|
$message_manager->write_message(
|
|
message => { is_error => 1, status => 400 } );
|
|
}
|
|
}
|
|
{
|
|
my $validator = validation_for(
|
|
params => {
|
|
message => { type => HashRef },
|
|
}
|
|
);
|
|
|
|
sub check_is_command {
|
|
my $self = shift;
|
|
my %params = $validator->(@_);
|
|
my $message = $params{message};
|
|
return 1 if exists $message->{command};
|
|
}
|
|
}
|
|
1
|