mirror of
https://github.com/pgbackrest/pgbackrest.git
synced 2024-12-14 10:13:05 +02:00
7248795b91
Replaced IPC::System::Simple and Net::OpenSSH with IPC::Open3 to eliminate CPAN dependency for multiple distros. Using open3 will also be used for local processes so it make sense to switch now.
289 lines
12 KiB
Perl
289 lines
12 KiB
Perl
####################################################################################################################################
|
|
# REMOTE MODULE
|
|
####################################################################################################################################
|
|
package BackRest::Remote;
|
|
use parent 'BackRest::Protocol';
|
|
|
|
use strict;
|
|
use warnings FATAL => qw(all);
|
|
use Carp qw(confess);
|
|
|
|
use Compress::Raw::Zlib qw(WANT_GZIP Z_OK Z_BUF_ERROR Z_STREAM_END);
|
|
use File::Basename qw(dirname);
|
|
|
|
use IO::String qw();
|
|
use POSIX qw(:sys_wait_h);
|
|
use Scalar::Util qw(blessed);
|
|
|
|
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;
|
|
use BackRest::Utility;
|
|
|
|
####################################################################################################################################
|
|
# Operation constants
|
|
####################################################################################################################################
|
|
use constant OP_REMOTE => 'Remote';
|
|
|
|
use constant OP_REMOTE_NEW => OP_REMOTE . "->new";
|
|
|
|
####################################################################################################################################
|
|
# Operation constants
|
|
####################################################################################################################################
|
|
use constant
|
|
{
|
|
OP_NOOP => 'noop',
|
|
OP_EXIT => 'exit'
|
|
};
|
|
|
|
####################################################################################################################################
|
|
# CONSTRUCTOR
|
|
####################################################################################################################################
|
|
sub new
|
|
{
|
|
my $class = shift; # Class name
|
|
my $strHost = shift; # Host to connect to for remote (optional as this can also be used on the remote)
|
|
my $strUser = shift; # User to connect to for remote (must be set if strHost is set)
|
|
my $strCommand = shift; # Command to execute on remote ('remote' if this is the 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_REMOTE_NEW, DEBUG_CALL, undef,
|
|
{host => $strHost, user => $strUser, command => $strCommand},
|
|
defined($strHost) ? DEBUG : TRACE);
|
|
|
|
# Init object and store variables
|
|
my $self = $class->SUPER::new(CMD_REMOTE, !defined($strHost), $strCommand, $iBlockSize, $iCompressLevel, $iCompressLevelNetwork,
|
|
$strHost, $strUser);
|
|
|
|
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->command_read(\%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->output_write(($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->output_write($strOutput);
|
|
}
|
|
# Create a path
|
|
elsif ($strCommand eq OP_FILE_PATH_CREATE)
|
|
{
|
|
$oFile->path_create(PATH_ABSOLUTE, paramGet(\%oParamHash, 'path'), paramGet(\%oParamHash, 'mode', false));
|
|
$self->output_write();
|
|
}
|
|
# Check if a file/path exists
|
|
elsif ($strCommand eq OP_FILE_EXISTS)
|
|
{
|
|
$self->output_write($oFile->exists(PATH_ABSOLUTE, paramGet(\%oParamHash, 'path')) ? 'Y' : 'N');
|
|
}
|
|
# Wait
|
|
elsif ($strCommand eq OP_FILE_WAIT)
|
|
{
|
|
$self->output_write($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->output_write($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->output_write("${strArchiveId}\t" . (defined($strChecksum) ? $strChecksum : 'Y'));
|
|
}
|
|
elsif ($strCommand eq OP_ARCHIVE_GET_CHECK)
|
|
{
|
|
$self->output_write($oArchive->getCheck($oFile));
|
|
}
|
|
# Info list stanza
|
|
elsif ($strCommand eq OP_INFO_LIST_STANZA)
|
|
{
|
|
$self->output_write(
|
|
$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->output_write("${strDbVersion}\t${iControlVersion}\t${iCatalogVersion}\t${ullDbSysId}");
|
|
}
|
|
elsif ($strCommand eq OP_DB_EXECUTE_SQL)
|
|
{
|
|
$self->output_write($oDb->executeSql(paramGet(\%oParamHash, 'script')));
|
|
}
|
|
# Continue if noop or exit
|
|
elsif ($strCommand ne OP_NOOP && $strCommand ne OP_EXIT)
|
|
{
|
|
confess "invalid command: ${strCommand}";
|
|
}
|
|
};
|
|
|
|
# Process errors
|
|
if ($@)
|
|
{
|
|
$self->error_write($@);
|
|
}
|
|
}
|
|
}
|
|
|
|
1;
|