2014-06-07 04:16:24 +03:00
|
|
|
####################################################################################################################################
|
|
|
|
# REMOTE MODULE
|
|
|
|
####################################################################################################################################
|
|
|
|
package pg_backrest_remote;
|
|
|
|
|
|
|
|
use threads;
|
|
|
|
use strict;
|
|
|
|
use warnings;
|
|
|
|
use Carp;
|
|
|
|
|
|
|
|
use Moose;
|
|
|
|
use Net::OpenSSH;
|
|
|
|
use File::Basename;
|
|
|
|
|
|
|
|
use lib dirname($0);
|
2014-06-07 18:51:27 +03:00
|
|
|
use lib dirname($0) . "/../lib";
|
|
|
|
use BackRest::Exception;
|
2014-06-07 04:16:24 +03:00
|
|
|
use pg_backrest_utility;
|
|
|
|
|
|
|
|
# Protocol strings
|
2014-06-07 23:06:46 +03:00
|
|
|
has strGreeting => (is => 'ro', default => 'PG_BACKREST_REMOTE');
|
2014-06-07 04:16:24 +03:00
|
|
|
|
|
|
|
# Command strings
|
|
|
|
has strCommand => (is => 'bare');
|
|
|
|
|
|
|
|
# Module variables
|
|
|
|
has strHost => (is => 'bare'); # Host host
|
|
|
|
has strUser => (is => 'bare'); # User user
|
|
|
|
has oSSH => (is => 'bare'); # SSH object
|
|
|
|
|
|
|
|
# Process variables
|
|
|
|
has pId => (is => 'bare'); # SSH object
|
|
|
|
has hIn => (is => 'bare'); # SSH object
|
|
|
|
has hOut => (is => 'bare'); # SSH object
|
|
|
|
has hErr => (is => 'bare'); # SSH object
|
|
|
|
|
|
|
|
####################################################################################################################################
|
|
|
|
# CONSTRUCTOR
|
|
|
|
####################################################################################################################################
|
|
|
|
sub BUILD
|
|
|
|
{
|
|
|
|
my $self = shift;
|
2014-06-07 23:06:46 +03:00
|
|
|
|
|
|
|
$self->{strGreeting} .= " " . version_get();
|
2014-06-07 04:16:24 +03:00
|
|
|
|
|
|
|
if (defined($self->{strHost}))
|
|
|
|
{
|
|
|
|
# User must be defined
|
|
|
|
if (!defined($self->{strUser}))
|
|
|
|
{
|
|
|
|
confess &log(ASSERT, "strUser must be defined");
|
|
|
|
}
|
|
|
|
|
|
|
|
# User must be defined
|
|
|
|
if (!defined($self->{strCommand}))
|
|
|
|
{
|
|
|
|
confess &log(ASSERT, "strCommand must be defined");
|
|
|
|
}
|
|
|
|
|
|
|
|
# Set SSH Options
|
|
|
|
my $strOptionSSHRequestTTY = "RequestTTY=yes";
|
|
|
|
my $strOptionSSHCompression = "Compression=no";
|
|
|
|
|
|
|
|
# if ($self->{bNoCompression})
|
|
|
|
# {
|
|
|
|
# $strOptionSSHCompression = "Compression=yes";
|
|
|
|
# }
|
|
|
|
|
2014-06-07 22:30:13 +03:00
|
|
|
&log(TRACE, "connecting to remote ssh host " . $self->{strHost});
|
2014-06-07 04:16:24 +03:00
|
|
|
|
|
|
|
# Make SSH connection
|
|
|
|
$self->{oSSH} = Net::OpenSSH->new($self->{strHost}, timeout => 300, user => $self->{strUser},
|
|
|
|
master_opts => [-o => $strOptionSSHCompression, -o => $strOptionSSHRequestTTY]);
|
|
|
|
|
|
|
|
$self->{oSSH}->error and confess &log(ERROR, "unable to connect to $self->{strHost}: " . $self->{oSSH}->error);
|
|
|
|
|
|
|
|
# Execute remote command
|
|
|
|
($self->{hIn}, $self->{hOut}, $self->{hErr}, $self->{pId}) = $self->{oSSH}->open3($self->{strCommand});
|
2014-06-07 22:30:13 +03:00
|
|
|
|
2014-06-07 22:01:29 +03:00
|
|
|
$self->greeting_read();
|
2014-06-07 04:16:24 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
####################################################################################################################################
|
|
|
|
# CLONE
|
|
|
|
####################################################################################################################################
|
|
|
|
sub clone
|
|
|
|
{
|
|
|
|
my $self = shift;
|
|
|
|
|
|
|
|
return pg_backrest_remote->new
|
|
|
|
(
|
|
|
|
strCommand => $self->{strCommand},
|
|
|
|
strHost => $self->{strUser},
|
|
|
|
strUser => $self->{strHost}
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
####################################################################################################################################
|
|
|
|
# GREETING_READ
|
|
|
|
####################################################################################################################################
|
|
|
|
sub greeting_read
|
|
|
|
{
|
|
|
|
my $self = shift;
|
|
|
|
|
|
|
|
# Make sure that the remote is running the right version
|
2014-06-07 22:01:29 +03:00
|
|
|
if (readline($self->{hOut}) ne $self->{strGreeting} . "\n")
|
2014-06-07 04:16:24 +03:00
|
|
|
{
|
|
|
|
confess &log(ERROR, "remote version mismatch");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
####################################################################################################################################
|
|
|
|
# GREETING_WRITE
|
|
|
|
####################################################################################################################################
|
|
|
|
sub greeting_write
|
|
|
|
{
|
|
|
|
my $self = shift;
|
|
|
|
|
|
|
|
if (!syswrite(*STDOUT, "$self->{strGreeting}\n"))
|
|
|
|
{
|
|
|
|
confess "unable to write greeting";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-06-07 18:51:27 +03:00
|
|
|
####################################################################################################################################
|
|
|
|
# STRING_WRITE
|
|
|
|
####################################################################################################################################
|
|
|
|
sub string_write
|
|
|
|
{
|
|
|
|
my $self = shift;
|
|
|
|
my $hOut = shift;
|
|
|
|
my $strBuffer = shift;
|
2014-06-07 22:30:13 +03:00
|
|
|
|
2014-06-07 18:51:27 +03:00
|
|
|
$strBuffer =~ s/\n/\n\./g;
|
2014-06-07 22:30:13 +03:00
|
|
|
|
2014-06-07 18:51:27 +03:00
|
|
|
if (!syswrite($hOut, "." . $strBuffer))
|
|
|
|
{
|
|
|
|
confess "unable to write string";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
####################################################################################################################################
|
|
|
|
# ERROR_WRITE
|
|
|
|
####################################################################################################################################
|
|
|
|
sub error_write
|
|
|
|
{
|
|
|
|
my $self = shift;
|
|
|
|
my $oMessage = shift;
|
2014-06-07 22:30:13 +03:00
|
|
|
|
2014-06-07 18:51:27 +03:00
|
|
|
my $iCode;
|
|
|
|
my $strMessage;
|
2014-06-07 22:30:13 +03:00
|
|
|
|
2014-06-07 18:51:27 +03:00
|
|
|
if (blessed($oMessage))
|
|
|
|
{
|
2014-06-07 22:30:13 +03:00
|
|
|
if ($oMessage->isa("BackRest::Exception"))
|
2014-06-07 18:51:27 +03:00
|
|
|
{
|
|
|
|
$iCode = $oMessage->code();
|
|
|
|
$strMessage = $oMessage->message();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
$strMessage = 'unknown error object';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
$strMessage = $oMessage;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (defined($strMessage))
|
|
|
|
{
|
|
|
|
$self->string_write(*STDOUT, trim($strMessage));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!syswrite(*STDOUT, "\nERROR" . (defined($iCode) ? " $iCode" : "") . "\n"))
|
|
|
|
{
|
|
|
|
confess "unable to write error";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
####################################################################################################################################
|
|
|
|
# OUTPUT_READ
|
|
|
|
####################################################################################################################################
|
|
|
|
sub output_read
|
|
|
|
{
|
|
|
|
my $self = shift;
|
|
|
|
|
2014-06-07 22:01:29 +03:00
|
|
|
# &log(TRACE, "Remote->output_read");
|
2014-06-07 20:15:55 +03:00
|
|
|
|
2014-06-07 18:51:27 +03:00
|
|
|
my $strLine;
|
|
|
|
my $strOutput;
|
|
|
|
my $bError = false;
|
|
|
|
my $iErrorCode;
|
|
|
|
|
|
|
|
while ($strLine = readline($self->{hOut}))
|
|
|
|
{
|
|
|
|
if ($strLine =~ /^ERROR.*/)
|
|
|
|
{
|
|
|
|
$bError = true;
|
2014-06-07 22:30:13 +03:00
|
|
|
|
2014-06-07 20:15:55 +03:00
|
|
|
$iErrorCode = (split(' ', trim($strLine)))[1];
|
2014-06-07 22:30:13 +03:00
|
|
|
|
2014-06-07 18:51:27 +03:00
|
|
|
last;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (trim($strLine) =~ /^OK$/)
|
|
|
|
{
|
|
|
|
last;
|
|
|
|
}
|
|
|
|
|
2014-06-07 22:01:29 +03:00
|
|
|
$strOutput .= (defined($strOutput) ? "\n" : "") . trim(substr($strLine, 1));
|
2014-06-07 18:51:27 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
return ($strOutput, $bError, $iErrorCode);
|
|
|
|
}
|
|
|
|
|
|
|
|
####################################################################################################################################
|
|
|
|
# OUTPUT_WRITE
|
|
|
|
####################################################################################################################################
|
|
|
|
sub output_write
|
|
|
|
{
|
|
|
|
my $self = shift;
|
|
|
|
my $strOutput = shift;
|
2014-06-07 22:30:13 +03:00
|
|
|
|
2014-06-07 18:51:27 +03:00
|
|
|
$self->string_write(*STDOUT, $strOutput);
|
|
|
|
|
|
|
|
if (!syswrite(*STDOUT, "\nOK\n"))
|
|
|
|
{
|
|
|
|
confess "unable to write output";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
####################################################################################################################################
|
2014-06-07 22:01:29 +03:00
|
|
|
# COMMAND_PARAM_STRING
|
2014-06-07 18:51:27 +03:00
|
|
|
####################################################################################################################################
|
2014-06-07 22:01:29 +03:00
|
|
|
sub command_param_string
|
2014-06-07 18:51:27 +03:00
|
|
|
{
|
|
|
|
my $self = shift;
|
2014-06-07 22:01:29 +03:00
|
|
|
my $oParamHashRef = shift;
|
2014-06-07 22:30:13 +03:00
|
|
|
|
2014-06-07 22:01:29 +03:00
|
|
|
my $strParamList;
|
2014-06-07 22:30:13 +03:00
|
|
|
|
2014-06-07 22:01:29 +03:00
|
|
|
foreach my $strParam (sort(keys $oParamHashRef))
|
2014-06-07 18:51:27 +03:00
|
|
|
{
|
2014-06-07 22:01:29 +03:00
|
|
|
$strParamList .= (defined($strParamList) ? "," : "") . "${strParam}=" .
|
|
|
|
(defined(${$oParamHashRef}{"${strParam}"}) ? ${$oParamHashRef}{"${strParam}"} : "[undef]");
|
2014-06-07 18:51:27 +03:00
|
|
|
}
|
2014-06-07 22:01:29 +03:00
|
|
|
|
|
|
|
return $strParamList;
|
|
|
|
}
|
|
|
|
|
|
|
|
####################################################################################################################################
|
|
|
|
# COMMAND_READ
|
|
|
|
####################################################################################################################################
|
|
|
|
sub command_read
|
|
|
|
{
|
|
|
|
my $self = shift;
|
|
|
|
my $oParamHashRef = shift;
|
|
|
|
|
|
|
|
# &log(TRACE, "Remote->command_read");
|
|
|
|
|
|
|
|
my $strLine;
|
|
|
|
my $strCommand;
|
|
|
|
|
|
|
|
while ($strLine = readline(*STDIN))
|
2014-06-07 18:51:27 +03:00
|
|
|
{
|
2014-06-07 22:01:29 +03:00
|
|
|
$strLine = trim($strLine);
|
2014-06-07 22:30:13 +03:00
|
|
|
|
2014-06-07 22:01:29 +03:00
|
|
|
if (!defined($strCommand))
|
|
|
|
{
|
|
|
|
if ($strLine =~ /:$/)
|
|
|
|
{
|
|
|
|
$strCommand = substr($strLine, 0, length($strLine) - 1);
|
|
|
|
# print "command ${strCommand} continues\n";
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
$strCommand = $strLine;
|
|
|
|
# print "command ${strCommand}\n";
|
|
|
|
last;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if ($strLine eq 'end')
|
|
|
|
{
|
|
|
|
last;
|
|
|
|
}
|
2014-06-07 22:30:13 +03:00
|
|
|
|
2014-06-07 22:01:29 +03:00
|
|
|
my $iPos = index($strLine, "=");
|
2014-06-07 22:30:13 +03:00
|
|
|
|
2014-06-07 22:01:29 +03:00
|
|
|
if ($iPos == -1)
|
|
|
|
{
|
|
|
|
confess "param \"${strLine}\" is missing = character";
|
|
|
|
}
|
2014-06-07 22:30:13 +03:00
|
|
|
|
2014-06-07 22:01:29 +03:00
|
|
|
my $strParam = substr($strLine, 0, $iPos);
|
|
|
|
my $strValue = substr($strLine, $iPos + 1);
|
2014-06-07 22:30:13 +03:00
|
|
|
|
2014-06-07 22:01:29 +03:00
|
|
|
${$oParamHashRef}{"${strParam}"} = ${strValue};
|
2014-06-07 22:30:13 +03:00
|
|
|
|
2014-06-07 22:01:29 +03:00
|
|
|
# print "${strParam}=${strValue}\n";
|
|
|
|
}
|
2014-06-07 18:51:27 +03:00
|
|
|
}
|
|
|
|
|
2014-06-07 22:01:29 +03:00
|
|
|
return $strCommand;
|
2014-06-07 18:51:27 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
####################################################################################################################################
|
|
|
|
# COMMAND_WRITE
|
|
|
|
####################################################################################################################################
|
|
|
|
sub command_write
|
|
|
|
{
|
|
|
|
my $self = shift;
|
|
|
|
my $strCommand = shift;
|
2014-06-07 22:01:29 +03:00
|
|
|
my $oParamRef = shift;
|
|
|
|
|
|
|
|
my $strOutput = $strCommand;
|
|
|
|
|
|
|
|
if (defined($oParamRef))
|
|
|
|
{
|
|
|
|
$strOutput = "${strCommand}:\n";
|
2014-06-07 22:30:13 +03:00
|
|
|
|
2014-06-07 22:01:29 +03:00
|
|
|
foreach my $strParam (sort(keys $oParamRef))
|
|
|
|
{
|
|
|
|
if ($strParam =~ /=/)
|
|
|
|
{
|
|
|
|
confess &log(ASSERT, "param \"${strParam}\" cannot contain = character");
|
|
|
|
}
|
|
|
|
|
|
|
|
my $strValue = ${$oParamRef}{"${strParam}"};
|
|
|
|
|
|
|
|
if ($strParam =~ /\n\$/)
|
|
|
|
{
|
|
|
|
confess &log(ASSERT, "param \"${strParam}\" value cannot end with LF");
|
|
|
|
}
|
2014-06-07 22:30:13 +03:00
|
|
|
|
2014-06-07 22:01:29 +03:00
|
|
|
if (defined(${strValue}))
|
|
|
|
{
|
|
|
|
$strOutput .= "${strParam}=${strValue}\n";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
$strOutput .= "end";
|
|
|
|
}
|
2014-06-07 18:51:27 +03:00
|
|
|
|
2014-06-07 22:30:13 +03:00
|
|
|
&log(TRACE, "Remote->command_write:\n" . trim($strOutput));
|
2014-06-07 20:15:55 +03:00
|
|
|
|
2014-06-07 22:01:29 +03:00
|
|
|
if (!syswrite($self->{hIn}, "${strOutput}\n"))
|
2014-06-07 18:51:27 +03:00
|
|
|
{
|
|
|
|
confess "unable to write command";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-06-07 20:15:55 +03:00
|
|
|
####################################################################################################################################
|
|
|
|
# COMMAND_EXECUTE
|
|
|
|
####################################################################################################################################
|
|
|
|
sub command_execute
|
|
|
|
{
|
|
|
|
my $self = shift;
|
|
|
|
my $strCommand = shift;
|
|
|
|
my $strOptions = shift;
|
|
|
|
my $strErrorPrefix = shift;
|
|
|
|
|
|
|
|
$self->command_write($strCommand, $strOptions);
|
2014-06-07 22:30:13 +03:00
|
|
|
|
2014-06-07 20:15:55 +03:00
|
|
|
my ($strOutput, $bError, $iErrorCode) = $self->output_read();
|
2014-06-07 22:30:13 +03:00
|
|
|
|
2014-06-07 20:15:55 +03:00
|
|
|
# Capture any errors
|
|
|
|
if ($bError)
|
|
|
|
{
|
|
|
|
confess &log(ERROR, (defined($strErrorPrefix) ? "${strErrorPrefix}" : "") .
|
|
|
|
(defined($strOutput) ? ": ${strOutput}" : ""), $iErrorCode);
|
|
|
|
}
|
2014-06-07 22:30:13 +03:00
|
|
|
|
2014-06-07 20:15:55 +03:00
|
|
|
return $strOutput;
|
|
|
|
}
|
|
|
|
|
2014-06-07 04:16:24 +03:00
|
|
|
no Moose;
|
|
|
|
__PACKAGE__->meta->make_immutable;
|