mirror of
https://github.com/pgbackrest/pgbackrest.git
synced 2024-12-14 10:13:05 +02:00
de7fc37f88
Refactor storage layer to allow for new repository filesystems using drivers. (Reviewed by Cynthia Shang.) Refactor IO layer to allow for new compression formats, checksum types, and other capabilities using filters. (Reviewed by Cynthia Shang.)
189 lines
6.6 KiB
Perl
189 lines
6.6 KiB
Perl
####################################################################################################################################
|
|
# Process Excecution, Management, and IO
|
|
####################################################################################################################################
|
|
package pgBackRest::Common::Io::Process;
|
|
|
|
use strict;
|
|
use warnings FATAL => qw(all);
|
|
use Carp qw(confess);
|
|
use English '-no_match_vars';
|
|
|
|
use Exporter qw(import);
|
|
our @EXPORT = qw();
|
|
use IPC::Open3 qw(open3);
|
|
use POSIX qw(:sys_wait_h);
|
|
use Symbol 'gensym';
|
|
|
|
use pgBackRest::Common::Exception;
|
|
use pgBackRest::Common::Io::Buffered;
|
|
use pgBackRest::Common::Log;
|
|
use pgBackRest::Common::Wait;
|
|
|
|
####################################################################################################################################
|
|
# Package name constant
|
|
####################################################################################################################################
|
|
use constant COMMON_IO_PROCESS => __PACKAGE__;
|
|
push @EXPORT, qw(COMMON_IO_PROCESS);
|
|
|
|
####################################################################################################################################
|
|
# Amount of time to attempt to retrieve errors when a process terminates unexpectedly
|
|
####################################################################################################################################
|
|
use constant IO_ERROR_TIMEOUT => 5;
|
|
|
|
####################################################################################################################################
|
|
# new - use open3 to run the command and get the io handles
|
|
####################################################################################################################################
|
|
our @ISA = (); ## no critic (ClassHierarchies::ProhibitExplicitISA)
|
|
|
|
sub new
|
|
{
|
|
my $class = shift;
|
|
|
|
# Assign function parameters, defaults, and log debug info
|
|
my
|
|
(
|
|
$strOperation,
|
|
$self,
|
|
$strCommand,
|
|
) =
|
|
logDebugParam
|
|
(
|
|
__PACKAGE__ . '->new', \@_,
|
|
{name => 'self', trace => true},
|
|
{name => 'strCommand', trace => true},
|
|
);
|
|
|
|
# Bless with new class
|
|
@ISA = $self->isA(); ## no critic (ClassHierarchies::ProhibitExplicitISA)
|
|
bless $self, $class;
|
|
|
|
# Use open3 to run the command
|
|
my ($iProcessId, $fhRead, $fhWrite, $fhReadError);
|
|
$fhReadError = gensym;
|
|
|
|
$iProcessId = IPC::Open3::open3($fhWrite, $fhRead, $fhReadError, $strCommand);
|
|
|
|
# Set handles
|
|
$self->handleReadSet($fhRead);
|
|
$self->handleWriteSet($fhWrite);
|
|
|
|
# Set variables
|
|
$self->{iProcessId} = $iProcessId;
|
|
$self->{fhReadError} = $fhReadError;
|
|
|
|
# Return from function and log return values if any
|
|
return logDebugReturn
|
|
(
|
|
$strOperation,
|
|
{name => 'self', value => $self}
|
|
);
|
|
}
|
|
|
|
####################################################################################################################################
|
|
# error - handle errors
|
|
####################################################################################################################################
|
|
sub error
|
|
{
|
|
my $self = shift;
|
|
my $iCode = shift;
|
|
my $strMessage = shift;
|
|
my $strDetail = shift;
|
|
my $bClose = shift;
|
|
|
|
if (defined($self->{iProcessId}))
|
|
{
|
|
my $oWait = waitInit(defined($iCode) ? IO_ERROR_TIMEOUT : 0);
|
|
|
|
do
|
|
{
|
|
# Check the result
|
|
my $iResult = waitpid($self->{iProcessId}, $bClose ? 0 : WNOHANG);
|
|
|
|
# Error if the process exited unexpectedly
|
|
if ($iResult != 0)
|
|
{
|
|
# Get the exit status
|
|
my $iExitStatus = $iResult == -1 ? 255 : ${^CHILD_ERROR_NATIVE} >> 8;
|
|
|
|
# Drain the stderr stream
|
|
my $strError;
|
|
my $oIoError = new pgBackRest::Common::Io::Buffered(
|
|
new pgBackRest::Common::Io::Handle($self->id(), $self->{fhReadError}), 5, $self->bufferMax());
|
|
|
|
while (defined(my $strLine = $oIoError->readLine(true, false)))
|
|
{
|
|
$strError .= "${strLine}\n";
|
|
}
|
|
|
|
delete($self->{iProcessId});
|
|
|
|
if (!$bClose || $iExitStatus != 0 || defined($strError))
|
|
{
|
|
my $iErrorCode =
|
|
$iExitStatus >= ERROR_MINIMUM && $iExitStatus <= ERROR_MAXIMUM ? $iExitStatus : ERROR_FILE_READ;
|
|
|
|
logErrorResult(
|
|
$iErrorCode, 'process ' . $self->id() . ' terminated unexpectedly' .
|
|
($iExitStatus != 255 ? sprintf(' [%03d]', $iExitStatus) : ''),
|
|
$strError);
|
|
}
|
|
}
|
|
}
|
|
while (waitMore($oWait));
|
|
|
|
if (defined($iCode))
|
|
{
|
|
$self->SUPER::error($iCode, $strMessage, $strDetail);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
confess &log(ASSERT, 'cannot call error() after process has been closed');
|
|
}
|
|
}
|
|
|
|
####################################################################################################################################
|
|
# Get process id
|
|
####################################################################################################################################
|
|
sub processId
|
|
{
|
|
my $self = shift;
|
|
|
|
return $self->{iProcessId};
|
|
}
|
|
|
|
####################################################################################################################################
|
|
# writeLine - check for error before writing line
|
|
####################################################################################################################################
|
|
sub writeLine
|
|
{
|
|
my $self = shift;
|
|
my $strBuffer = shift;
|
|
|
|
# Check if the process has exited abnormally (doesn't seem like we should need this, but the next syswrite does a hard
|
|
# abort if the remote process has already closed)
|
|
$self->error();
|
|
|
|
return $self->SUPER::writeLine($strBuffer);
|
|
}
|
|
|
|
####################################################################################################################################
|
|
# close - check if the process terminated on error
|
|
####################################################################################################################################
|
|
sub close
|
|
{
|
|
my $self = shift;
|
|
|
|
if (defined($self->{iProcessId}))
|
|
{
|
|
$self->error(undef, undef, undef, true);
|
|
|
|
# Class parent close
|
|
$self->SUPER::close();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
1;
|