1
0
mirror of https://github.com/pgbackrest/pgbackrest.git synced 2024-12-14 10:13:05 +02:00
pgbackrest/lib/pgBackRest/Protocol/CommonMaster.pm

285 lines
9.7 KiB
Perl

####################################################################################################################################
# PROTOCOL COMMON MASTER MODULE
####################################################################################################################################
package pgBackRest::Protocol::CommonMaster;
use parent 'pgBackRest::Protocol::Common';
use strict;
use warnings FATAL => qw(all);
use Carp qw(confess);
use English '-no_match_vars';
use File::Basename qw(dirname);
use Time::HiRes qw(gettimeofday);
use pgBackRest::Common::Exception;
use pgBackRest::Common::Ini;
use pgBackRest::Common::Log;
use pgBackRest::Protocol::Common;
use pgBackRest::Version;
####################################################################################################################################
# CONSTRUCTOR
####################################################################################################################################
sub new
{
my $class = shift; # Class name
# Assign function parameters, defaults, and log debug info
my
(
$strOperation,
$strRemoteType, # Type of remote (DB or BACKUP)
$strName, # Name of the protocol
$strId, # Id of this process for error messages
$oIO, # IO object
$iBufferMax, # Maximum buffer size
$iCompressLevel, # Set compression level
$iCompressLevelNetwork, # Set compression level for network only compression
$iProtocolTimeout, # Protocol timeout
) =
logDebugParam
(
__PACKAGE__ . '->new', \@_,
{name => 'strRemoteType', trace => true},
{name => 'strName', trace => true},
{name => 'strId', trace => true},
{name => 'strCommand', trace => true},
{name => 'iBufferMax', trace => true},
{name => 'iCompressLevel', trace => true},
{name => 'iCompressLevelNetwork', trace => true},
{name => 'iProtocolTimeout', trace => true},
);
# Create the class hash
my $self = $class->SUPER::new($iBufferMax, $iCompressLevel, $iCompressLevelNetwork, $iProtocolTimeout, $strName);
bless $self, $class;
# Set remote to specified value
$self->{strRemoteType} = $strRemoteType;
# Set IO Object
$self->{io} = $oIO;
# Check greeting to be sure the protocol matches
$self->greetingRead();
# Setup the keepalive timer
$self->{fKeepAliveTimeout} = $iProtocolTimeout / 2 > 120 ? 120 : $iProtocolTimeout / 2;
$self->{fKeepAliveTime} = gettimeofday();
# Set the id to be used for messages (especially error messages)
$self->{strId} = $strId;
# Set the error prefix used when raising error messages
$self->{strErrorPrefix} = 'raised on ' . $self->{strId} . ' host';
# Return from function and log return values if any
return logDebugReturn
(
$strOperation,
{name => 'self', value => $self, trace => true}
);
}
####################################################################################################################################
# DESTROY
####################################################################################################################################
sub DESTROY
{
my $self = shift;
$self->close();
}
####################################################################################################################################
# greetingRead
#
# Read the greeting and make sure it is as expected.
####################################################################################################################################
sub greetingRead
{
my $self = shift;
# Get the first line of output from the remote if possible
my $hGreeting = $self->{oJSON}->decode($self->{io}->lineRead(undef, undef, undef, true));
# Error if greeting parameters do not match
for my $hParam ({strName => 'name', strExpected => BACKREST_NAME},
{strName => 'version', strExpected => BACKREST_VERSION},
{strName => 'service', strExpected => $self->{strName}})
{
if (!defined($hGreeting->{$hParam->{strName}}) || $hGreeting->{$hParam->{strName}} ne $hParam->{strExpected})
{
confess &log(ERROR,
'found name \'' . (defined($hGreeting->{$hParam->{strName}}) ? $hGreeting->{$hParam->{strName}} : '[undef]') .
"' in protocol greeting instead of expected '$hParam->{strExpected}'", ERROR_HOST_CONNECT);
}
}
# Exchange one protocol message to catch errors early
$self->outputRead();
}
####################################################################################################################################
# outputRead
#
# Read output from the remote process.
####################################################################################################################################
sub outputRead
{
my $self = shift;
# Assign function parameters, defaults, and log debug info
my
(
$strOperation,
$bOutputRequired,
$bSuppressLog,
$bWarnOnError,
$bRef,
) =
logDebugParam
(
__PACKAGE__ . '->outputRead', \@_,
{name => 'bOutputRequired', default => false, trace => true},
{name => 'bSuppressLog', required => false, trace => true},
{name => 'bWarnOnError', default => false, trace => true},
{name => 'bRef', default => false, trace => true},
);
my $strProtocolResult = $self->{io}->lineRead();
logDebugMisc
(
$strOperation, undef,
{name => 'strProtocolResult', value => $strProtocolResult, trace => true}
);
my $hResult = $self->{oJSON}->decode($strProtocolResult);
# Raise any errors
if (defined($hResult->{err}))
{
my $strError = $self->{strErrorPrefix} . (defined($hResult->{out}) ? ": $hResult->{out}" : '');
# Raise the error if a warning is not requested
if (!$bWarnOnError)
{
confess &log(ERROR, $strError, $hResult->{err}, $bSuppressLog);
}
&log(WARN, $strError, $hResult->{err});
undef($hResult->{out});
}
# Reset the keep alive time
$self->{fKeepAliveTime} = gettimeofday();
# If output is required and there is no output, raise exception
if ($bOutputRequired && !defined($hResult->{out}))
{
$self->{io}->waitPid();
confess &log(ERROR, "$self->{strErrorPrefix}: output is not defined", ERROR_PROTOCOL_OUTPUT_REQUIRED);
}
# Return from function and log return values if any
return logDebugReturn
(
$strOperation,
{name => 'hOutput', value => $hResult->{out}, ref => $bRef, trace => true}
);
}
####################################################################################################################################
# cmdWrite
#
# Send command to remote process.
####################################################################################################################################
sub cmdWrite
{
my $self = shift;
# Assign function parameters, defaults, and log debug info
my
(
$strOperation,
$strCommand,
$hParam,
) =
logDebugParam
(
__PACKAGE__ . '->cmdWrite', \@_,
{name => 'strCommand', trace => true},
{name => 'hParam', required => false, trace => true},
);
my $strProtocolCommand = $self->{oJSON}->encode({cmd => $strCommand, param => $hParam});
logDebugMisc
(
$strOperation, undef,
{name => 'strProtocolCommand', value => $strProtocolCommand, trace => true}
);
# Write out the command
$self->{io}->lineWrite($strProtocolCommand);
# Reset the keep alive time
$self->{fKeepAliveTime} = gettimeofday();
# Return from function and log return values if any
logDebugReturn($strOperation);
}
####################################################################################################################################
# cmdExecute
#
# Send command to remote process and wait for output.
####################################################################################################################################
sub cmdExecute
{
my $self = shift;
my $strCommand = shift;
my $oParamRef = shift;
my $bOutputRequired = shift;
my $bWarnOnError = shift;
$self->cmdWrite($strCommand, $oParamRef);
return $self->outputRead($bOutputRequired, undef, $bWarnOnError);
}
####################################################################################################################################
# keepAlive
#
# Send periodic noops so the remote does not time out.
####################################################################################################################################
sub keepAlive
{
my $self = shift;
if (gettimeofday() - $self->{fKeepAliveTimeout} > $self->{fKeepAliveTime})
{
$self->noOp();
# Keep alive test point
&log(TEST, TEST_KEEP_ALIVE);
}
}
####################################################################################################################################
# noOp
#
# Send noop to test connection or keep it alive.
####################################################################################################################################
sub noOp
{
my $self = shift;
$self->cmdExecute(OP_NOOP, undef, false);
$self->{fKeepAliveTime} = gettimeofday();
}
1;