You've already forked pgbackrest
mirror of
https://github.com/pgbackrest/pgbackrest.git
synced 2025-12-21 23:47:33 +02:00
* 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.
281 lines
12 KiB
Perl
281 lines
12 KiB
Perl
####################################################################################################################################
|
|
# PROTOCOL REMOTE MINION MODULE
|
|
####################################################################################################################################
|
|
package BackRest::Protocol::RemoteMinion;
|
|
use parent 'BackRest::Protocol::CommonMinion';
|
|
|
|
use strict;
|
|
use warnings FATAL => qw(all);
|
|
use Carp qw(confess);
|
|
|
|
use File::Basename qw(dirname);
|
|
|
|
use lib dirname($0) . '/../lib';
|
|
use BackRest::Archive;
|
|
use BackRest::Config;
|
|
use BackRest::Db;
|
|
use BackRest::Exception;
|
|
use BackRest::File;
|
|
use BackRest::Info;
|
|
use BackRest::Protocol::CommonMinion;
|
|
use BackRest::Utility;
|
|
|
|
####################################################################################################################################
|
|
# Operation constants
|
|
####################################################################################################################################
|
|
use constant OP_PROTOCOL_REMOVE_MINION => 'Protocol::RemoteMinion';
|
|
|
|
use constant OP_PROTOCOL_REMOVE_MINION_NEW => OP_PROTOCOL_REMOVE_MINION . "->new";
|
|
|
|
####################################################################################################################################
|
|
# Operation constants
|
|
####################################################################################################################################
|
|
use constant
|
|
{
|
|
OP_NOOP => 'noop',
|
|
OP_EXIT => 'exit'
|
|
};
|
|
|
|
####################################################################################################################################
|
|
# CONSTRUCTOR
|
|
####################################################################################################################################
|
|
sub new
|
|
{
|
|
my $class = shift; # Class name
|
|
my $iBlockSize = shift; # Buffer size
|
|
my $iCompressLevel = shift; # Set compression level
|
|
my $iCompressLevelNetwork = shift; # Set compression level for network only compression
|
|
|
|
# Debug
|
|
logTrace(OP_PROTOCOL_REMOVE_MINION_NEW, DEBUG_CALL, undef,
|
|
{iBlockSize => $iBlockSize, iCompressLevel => $iCompressLevel, iCompressNetworkLevel => $iCompressLevelNetwork});
|
|
|
|
# Init object and store variables
|
|
my $self = $class->SUPER::new(CMD_REMOTE, $iBlockSize, $iCompressLevel, $iCompressLevelNetwork);
|
|
bless $self, $class;
|
|
|
|
return $self;
|
|
}
|
|
|
|
####################################################################################################################################
|
|
# paramGet
|
|
#
|
|
# Helper function that returns the param or an error if required and it does not exist.
|
|
####################################################################################################################################
|
|
sub paramGet
|
|
{
|
|
my $oParamHashRef = shift;
|
|
my $strParam = shift;
|
|
my $bRequired = shift;
|
|
|
|
my $strValue = ${$oParamHashRef}{$strParam};
|
|
|
|
if (!defined($strValue) && (!defined($bRequired) || $bRequired))
|
|
{
|
|
confess "${strParam} must be defined";
|
|
}
|
|
|
|
return $strValue;
|
|
}
|
|
|
|
####################################################################################################################################
|
|
# process
|
|
####################################################################################################################################
|
|
sub process
|
|
{
|
|
my $self = shift;
|
|
|
|
# Create the file object
|
|
my $oFile = new BackRest::File
|
|
(
|
|
optionGet(OPTION_STANZA, false),
|
|
optionGet(OPTION_REPO_REMOTE_PATH, false),
|
|
undef,
|
|
$self,
|
|
);
|
|
|
|
# Create objects
|
|
my $oArchive = new BackRest::Archive();
|
|
my $oInfo = new BackRest::Info();
|
|
my $oJSON = JSON::PP->new();
|
|
my $oDb = new BackRest::Db();
|
|
|
|
# Command string
|
|
my $strCommand = OP_NOOP;
|
|
|
|
# Loop until the exit command is received
|
|
while ($strCommand ne OP_EXIT)
|
|
{
|
|
my %oParamHash;
|
|
|
|
$strCommand = $self->cmdRead(\%oParamHash);
|
|
|
|
eval
|
|
{
|
|
# Copy file
|
|
if ($strCommand eq OP_FILE_COPY ||
|
|
$strCommand eq OP_FILE_COPY_IN ||
|
|
$strCommand eq OP_FILE_COPY_OUT)
|
|
{
|
|
my $bResult;
|
|
my $strChecksum;
|
|
my $iFileSize;
|
|
|
|
# Copy a file locally
|
|
if ($strCommand eq OP_FILE_COPY)
|
|
{
|
|
($bResult, $strChecksum, $iFileSize) =
|
|
$oFile->copy(PATH_ABSOLUTE, paramGet(\%oParamHash, 'source_file'),
|
|
PATH_ABSOLUTE, paramGet(\%oParamHash, 'destination_file'),
|
|
paramGet(\%oParamHash, 'source_compressed'),
|
|
paramGet(\%oParamHash, 'destination_compress'),
|
|
paramGet(\%oParamHash, 'ignore_missing_source', false),
|
|
undef,
|
|
paramGet(\%oParamHash, 'mode', false),
|
|
paramGet(\%oParamHash, 'destination_path_create') ? 'Y' : 'N',
|
|
paramGet(\%oParamHash, 'user', false),
|
|
paramGet(\%oParamHash, 'group', false),
|
|
paramGet(\%oParamHash, 'append_checksum', false));
|
|
}
|
|
# Copy a file from STDIN
|
|
elsif ($strCommand eq OP_FILE_COPY_IN)
|
|
{
|
|
($bResult, $strChecksum, $iFileSize) =
|
|
$oFile->copy(PIPE_STDIN, undef,
|
|
PATH_ABSOLUTE, paramGet(\%oParamHash, 'destination_file'),
|
|
paramGet(\%oParamHash, 'source_compressed'),
|
|
paramGet(\%oParamHash, 'destination_compress'),
|
|
undef, undef,
|
|
paramGet(\%oParamHash, 'mode', false),
|
|
paramGet(\%oParamHash, 'destination_path_create'),
|
|
paramGet(\%oParamHash, 'user', false),
|
|
paramGet(\%oParamHash, 'group', false),
|
|
paramGet(\%oParamHash, 'append_checksum', false));
|
|
}
|
|
# Copy a file to STDOUT
|
|
elsif ($strCommand eq OP_FILE_COPY_OUT)
|
|
{
|
|
($bResult, $strChecksum, $iFileSize) =
|
|
$oFile->copy(PATH_ABSOLUTE, paramGet(\%oParamHash, 'source_file'),
|
|
PIPE_STDOUT, undef,
|
|
paramGet(\%oParamHash, 'source_compressed'),
|
|
paramGet(\%oParamHash, 'destination_compress'));
|
|
}
|
|
|
|
$self->outputWrite(($bResult ? 'Y' : 'N') . " " . (defined($strChecksum) ? $strChecksum : '?') . " " .
|
|
(defined($iFileSize) ? $iFileSize : '?'));
|
|
}
|
|
# List files in a path
|
|
elsif ($strCommand eq OP_FILE_LIST)
|
|
{
|
|
my $strOutput;
|
|
|
|
foreach my $strFile ($oFile->list(PATH_ABSOLUTE, paramGet(\%oParamHash, 'path'),
|
|
paramGet(\%oParamHash, 'expression', false),
|
|
paramGet(\%oParamHash, 'sort_order'),
|
|
paramGet(\%oParamHash, 'ignore_missing')))
|
|
{
|
|
if (defined($strOutput))
|
|
{
|
|
$strOutput .= "\n";
|
|
}
|
|
|
|
$strOutput .= $strFile;
|
|
}
|
|
|
|
$self->outputWrite($strOutput);
|
|
}
|
|
# Create a path
|
|
elsif ($strCommand eq OP_FILE_PATH_CREATE)
|
|
{
|
|
$oFile->path_create(PATH_ABSOLUTE, paramGet(\%oParamHash, 'path'), paramGet(\%oParamHash, 'mode', false));
|
|
$self->outputWrite();
|
|
}
|
|
# Check if a file/path exists
|
|
elsif ($strCommand eq OP_FILE_EXISTS)
|
|
{
|
|
$self->outputWrite($oFile->exists(PATH_ABSOLUTE, paramGet(\%oParamHash, 'path')) ? 'Y' : 'N');
|
|
}
|
|
# Wait
|
|
elsif ($strCommand eq OP_FILE_WAIT)
|
|
{
|
|
$self->outputWrite($oFile->wait(PATH_ABSOLUTE));
|
|
}
|
|
# Generate a manifest
|
|
elsif ($strCommand eq OP_FILE_MANIFEST)
|
|
{
|
|
my %oManifestHash;
|
|
|
|
$oFile->manifest(PATH_ABSOLUTE, paramGet(\%oParamHash, 'path'), \%oManifestHash);
|
|
|
|
my $strOutput = "name\ttype\tuser\tgroup\tmode\tmodification_time\tinode\tsize\tlink_destination";
|
|
|
|
foreach my $strName (sort(keys(%{$oManifestHash{name}})))
|
|
{
|
|
$strOutput .= "\n${strName}\t" .
|
|
$oManifestHash{name}{"${strName}"}{type} . "\t" .
|
|
(defined($oManifestHash{name}{"${strName}"}{user}) ? $oManifestHash{name}{"${strName}"}{user} : "") . "\t" .
|
|
(defined($oManifestHash{name}{"${strName}"}{group}) ? $oManifestHash{name}{"${strName}"}{group} : "") . "\t" .
|
|
(defined($oManifestHash{name}{"${strName}"}{mode}) ? $oManifestHash{name}{"${strName}"}{mode} : "") . "\t" .
|
|
(defined($oManifestHash{name}{"${strName}"}{modification_time}) ?
|
|
$oManifestHash{name}{"${strName}"}{modification_time} : "") . "\t" .
|
|
(defined($oManifestHash{name}{"${strName}"}{inode}) ? $oManifestHash{name}{"${strName}"}{inode} : "") . "\t" .
|
|
(defined($oManifestHash{name}{"${strName}"}{size}) ? $oManifestHash{name}{"${strName}"}{size} : "") . "\t" .
|
|
(defined($oManifestHash{name}{"${strName}"}{link_destination}) ?
|
|
$oManifestHash{name}{"${strName}"}{link_destination} : "");
|
|
}
|
|
|
|
$self->outputWrite($strOutput);
|
|
}
|
|
# Archive push checks
|
|
elsif ($strCommand eq OP_ARCHIVE_PUSH_CHECK)
|
|
{
|
|
my ($strArchiveId, $strChecksum) = $oArchive->pushCheck($oFile,
|
|
paramGet(\%oParamHash, 'wal-segment'),
|
|
undef,
|
|
paramGet(\%oParamHash, 'db-version'),
|
|
paramGet(\%oParamHash, 'db-sys-id'));
|
|
|
|
$self->outputWrite("${strArchiveId}\t" . (defined($strChecksum) ? $strChecksum : 'Y'));
|
|
}
|
|
elsif ($strCommand eq OP_ARCHIVE_GET_CHECK)
|
|
{
|
|
$self->outputWrite($oArchive->getCheck($oFile));
|
|
}
|
|
# Info list stanza
|
|
elsif ($strCommand eq OP_INFO_LIST_STANZA)
|
|
{
|
|
$self->outputWrite(
|
|
$oJSON->encode(
|
|
$oInfo->listStanza($oFile,
|
|
paramGet(\%oParamHash, 'stanza', false))));
|
|
}
|
|
elsif ($strCommand eq OP_DB_INFO)
|
|
{
|
|
my ($strDbVersion, $iControlVersion, $iCatalogVersion, $ullDbSysId) =
|
|
$oDb->info($oFile, paramGet(\%oParamHash, 'db-path'));
|
|
|
|
$self->outputWrite("${strDbVersion}\t${iControlVersion}\t${iCatalogVersion}\t${ullDbSysId}");
|
|
}
|
|
elsif ($strCommand eq OP_DB_EXECUTE_SQL)
|
|
{
|
|
$self->outputWrite($oDb->executeSql(paramGet(\%oParamHash, 'script'),
|
|
paramGet(\%oParamHash, 'ignore-error', false)));
|
|
}
|
|
# Continue if noop or exit
|
|
elsif ($strCommand ne OP_NOOP && $strCommand ne OP_EXIT)
|
|
{
|
|
confess "invalid command: ${strCommand}";
|
|
}
|
|
};
|
|
|
|
# Process errors
|
|
if ($@)
|
|
{
|
|
$self->errorWrite($@);
|
|
}
|
|
}
|
|
}
|
|
|
|
1;
|