1
0
mirror of https://github.com/pgbackrest/pgbackrest.git synced 2025-12-21 23:47:33 +02:00
Files
pgbackrest/lib/BackRest/Protocol/CommonMaster.pm
David Steele a59284a4cc v0.80: DBI Support, Stability, and Convenience Features
* Fixed an issue that caused the formatted timestamp for both the oldest and newest backups to be reported as the current time by the info command.  Only text output was affected -- json output reported the correct epoch values.  Reported by Michael Renner.

* Fixed protocol issue that was preventing ssh errors (especially on connection) from being logged.

* Now using Perl DBI and DBD::Pg for connections to PostgreSQL rather than psql.  The cmd-psql and cmd-psql-option settings have been removed and replaced with db-port and db-socket-path.

* Add stop-auto option to allow failed backups to automatically be stopped when a new backup starts.

* Add db-timeout option to limit the amount of time pgBackRest will wait for pg_start_backup() and pg_stop_backup() to return.

* Remove pg_control file at the beginning of the restore and copy it back at the very end.  This prevents the possibility that a partial restore can be started by PostgreSQL.

* The repository is now created and updated with consistent directory and file modes.  By default umask is set to 0000 but this can be disabled with the neutral-umask setting.

* Added checks to be sure the db-path setting is consistent with db-port by comparing the data_directory as reported by the cluster against the db-path setting and the version as reported by the cluster against the value read from pg_control.  The db-socket-path setting is checked to be sure it is an absolute path.

* Experimental support for PostgreSQL 9.5 alpha1.  This may break when the control version or WAL magic changes in future versions but will be updated in each pgBackRest release to keep pace.  All regression tests pass except for --target-resume tests (this functionality has changed in 9.5) and there is no testing yet for .partial WAL segments.

* Major refactoring of the protocol layer to support future development.

* Added vagrant test configurations for Ubuntu 14.04 and CentOS 7.

* Split most of README.md out into USERGUIDE.md and CHANGELOG.md because it was becoming unwieldy.  Changed most references to "database" in the user guide to "database cluster" for clarity.
2015-08-09 11:20:43 -04:00

262 lines
8.9 KiB
Perl

####################################################################################################################################
# PROTOCOL COMMON MASTER MODULE
####################################################################################################################################
package BackRest::Protocol::CommonMaster;
use parent 'BackRest::Protocol::Common';
use strict;
use warnings FATAL => qw(all);
use Carp qw(confess);
use File::Basename qw(dirname);
use lib dirname($0) . '/../lib';
use BackRest::Config;
use BackRest::Exception;
use BackRest::Ini;
use BackRest::Protocol::Common;
use BackRest::Protocol::IO;
use BackRest::Utility;
####################################################################################################################################
# Operation constants
####################################################################################################################################
use constant OP_PROTOCOL_COMMON_MASTER => 'Protocol::CommonMaster';
use constant OP_PROTOCOL_COMMON_MASTER_NEW => OP_PROTOCOL_COMMON_MASTER . "->new";
use constant OP_PROTOCOL_COMMON_MASTER_COMMAND_WRITE => OP_PROTOCOL_COMMON_MASTER . "->commandWrite";
use constant OP_PROTOCOL_COMMON_MASTER_OUTPUT_READ => OP_PROTOCOL_COMMON_MASTER . "->outputRead";
####################################################################################################################################
# CONSTRUCTOR
####################################################################################################################################
sub new
{
my $class = shift; # Class name
my $strName = shift; # Name of the protocol
my $strCommand = shift; # Command to execute on local/remote
my $iBlockSize = shift; # Buffer size
my $iCompressLevel = shift; # Set compression level
my $iCompressLevelNetwork = shift; # Set compression level for network only compression
# Debug
logDebug(OP_PROTOCOL_COMMON_MASTER_NEW, DEBUG_CALL, undef,
{name => \$strName, command => \$strCommand, blockSize => $iBlockSize,
compressLevel => $iCompressLevel, compressLevelNetwork => $iCompressLevelNetwork});
# Create the class hash
my $self = $class->SUPER::new($iBlockSize, $iCompressLevel, $iCompressLevelNetwork, $strName);
bless $self, $class;
# Set command
if (!defined($strCommand))
{
confess &log(ASSERT, 'strCommand must be set');
}
# Execute the command
$self->{io} = BackRest::Protocol::IO->new3($strCommand);
# Check greeting to be sure the protocol matches
$self->greetingRead();
return $self;
}
####################################################################################################################################
# DESTROY
####################################################################################################################################
sub DESTROY
{
my $self = shift;
# Only send the exit command if the process is running
if (defined($self->{io}) && defined($self->{io}->pIdGet()))
{
&log(TRACE, "sending exit command to process");
$self->cmdWrite('exit');
# &log(TRACE, "waiting for remote process");
# if (!$self->waitPid(5, false))
# {
# &log(TRACE, "killed remote process");
# kill('KILL', $self->{pId});
# }
}
}
####################################################################################################################################
# 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 $strLine = $self->{io}->lineRead();
# If the line could not be read or does equal the greeting then error and exit
if (!defined($strLine) || $strLine ne $self->{strGreeting})
{
$self->{io}->kill();
confess &log(ERROR, 'protocol version mismatch' . (defined($strLine) ? ": ${strLine}" : ''), ERROR_HOST_CONNECT);
}
}
####################################################################################################################################
# outputRead
#
# Read output from the remote process.
####################################################################################################################################
sub outputRead
{
my $self = shift;
my $bOutputRequired = shift;
my $strErrorPrefix = shift;
my $bSuppressLog = shift;
logTrace(OP_PROTOCOL_COMMON_MASTER_OUTPUT_READ, DEBUG_CALL, undef,
{isOutputRequired => $bOutputRequired, strErrorPrefix => \$strErrorPrefix, isSuppressLog => $bSuppressLog});
my $strLine;
my $strOutput;
my $bError = false;
my $iErrorCode;
my $strError;
# Read output lines
while ($strLine = $self->{io}->lineRead(false))
{
# Exit if an error is found
if ($strLine =~ /^ERROR.*/)
{
$bError = true;
$iErrorCode = (split(' ', $strLine))[1];
last;
}
# Exit if OK is found
if ($strLine =~ /^OK$/)
{
last;
}
# Else append to output
$strOutput .= (defined($strOutput) ? "\n" : '') . substr($strLine, 1);
}
# Check if the process has exited abnormally
$self->{io}->waitPid();
# Raise any errors
if ($bError)
{
confess &log(ERROR, (defined($strErrorPrefix) ? "${strErrorPrefix}: " : '') .
(defined($strOutput) ? "${strOutput}" : ''), $iErrorCode, $bSuppressLog);
}
# If output is required and there is no output, raise exception
if (defined($bOutputRequired) && $bOutputRequired && !defined($strOutput))
{
confess &log(ERROR, (defined($strErrorPrefix) ? "${strErrorPrefix}: " : '') . 'output is not defined');
}
logTrace(OP_PROTOCOL_COMMON_MASTER_OUTPUT_READ, DEBUG_RESULT, undef, {strOutput => \$strOutput});
# Return output
return $strOutput;
}
####################################################################################################################################
# commandParamString
#
# Output command parameters in the hash as a string (used for debugging).
####################################################################################################################################
sub commandParamString
{
my $self = shift;
my $oParamHashRef = shift;
my $strParamList = '';
if (defined($oParamHashRef))
{
foreach my $strParam (sort(keys(%$oParamHashRef)))
{
$strParamList .= (defined($strParamList) ? ',' : '') . "${strParam}=" .
(defined(${$oParamHashRef}{"${strParam}"}) ? ${$oParamHashRef}{"${strParam}"} : '[undef]');
}
}
return $strParamList;
}
####################################################################################################################################
# cmdWrite
#
# Send command to remote process.
####################################################################################################################################
sub cmdWrite
{
my $self = shift;
my $strCommand = shift;
my $oParamRef = shift;
logTrace(OP_PROTOCOL_COMMON_MASTER_COMMAND_WRITE, DEBUG_CALL, \$strCommand, $oParamRef);
if (defined($oParamRef))
{
$strCommand .= ":\n";
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");
}
if (defined(${strValue}))
{
$strCommand .= "${strParam}=${strValue}\n";
}
}
$strCommand .= 'end';
}
$self->{io}->lineWrite($strCommand);
logTrace(OP_PROTOCOL_COMMON_MASTER_COMMAND_WRITE, DEBUG_RESULT, undef, {strCommand => \$strCommand});
}
####################################################################################################################################
# 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 $strErrorPrefix = shift;
$self->cmdWrite($strCommand, $oParamRef);
return $self->outputRead($bOutputRequired, $strErrorPrefix);
}
1;