diff --git a/bin/pg_backrest_remote.pl b/bin/pg_backrest_remote.pl index be3a8c6c5..ce14f827e 100755 --- a/bin/pg_backrest_remote.pl +++ b/bin/pg_backrest_remote.pl @@ -65,9 +65,42 @@ use constant # Turn off logging log_level_set(OFF, OFF); +# Creat the file object +my $oFile = pg_backrest_file->new(); + +# Create the remote object for writing to stdout my $oRemote = pg_backrest_remote->new(); -$oRemote->welcome_send(); +# Write the greeting so remote process knows who we are +$oRemote->greeting_write(); + +# Get the first command +my ($strCommand, $strOptions) = $oRemote->command_read(); + +# Loop until the exit command is received +while ($strCommand ne 'exit') +{ + eval + { + # File->exists + if ($strCommand eq OP_EXISTS) + { + $oRemote->output_write($oFile->exists(PATH_ABSOLUTE, $strOptions) ? "Y" : "N"); + } + else + { + confess "invalid command: ${strCommand}"; + } + }; + + if ($@) + { + $oRemote->error_write($@); + } + + # Get the next command + ($strCommand, $strOptions) = $oRemote->command_read(); +} # # Get the operation # my $strOperation = $ARGV[0]; diff --git a/lib/BackRest/Exception.pm b/lib/BackRest/Exception.pm new file mode 100644 index 000000000..c9815a95a --- /dev/null +++ b/lib/BackRest/Exception.pm @@ -0,0 +1,38 @@ +#################################################################################################################################### +# EXCEPTION MODULE +#################################################################################################################################### +package BackRest::Exception; + +use threads; +use strict; +use warnings; +use Carp; + +use Moose; + +# Module variables +has iCode => (is => 'bare'); # Exception code +has strMessage => (is => 'bare'); # Exception message + +#################################################################################################################################### +# CODE +#################################################################################################################################### +sub code +{ + my $self = shift; + + return $self->{iCode}; +} + +#################################################################################################################################### +# MESSAGE +#################################################################################################################################### +sub message +{ + my $self = shift; + + return $self->{strMessage}; +} + +no Moose; +__PACKAGE__->meta->make_immutable; diff --git a/pg_backrest_file.pm b/pg_backrest_file.pm index 85ba0cb25..9dc57c05d 100644 --- a/pg_backrest_file.pm +++ b/pg_backrest_file.pm @@ -21,6 +21,8 @@ use IO::Uncompress::Gunzip qw(gunzip $GunzipError); use IO::String; use lib dirname($0); +use lib dirname($0) . "/../lib"; +use BackRest::Exception; use pg_backrest_utility; use pg_backrest_remote; @@ -1126,7 +1128,7 @@ sub exists my $strPath = shift; # Set error prefix, remote, and path - my $bExists = false; + my $bExists = true; my $strErrorPrefix = "File->exists"; my $bRemote = $self->is_remote($strPathType); my $strPathOp = $self->path_get($strPathType, $strPath); @@ -1136,19 +1138,17 @@ sub exists # Run remotely if ($bRemote) { - my $strCommand = $self->{strCommand} . " exists ${strPathOp}"; - -# syswrite(self->{hIn}, ) - # Build the command + my $strCommand = "EXISTS:${strPathOp}"; + + $self->{oRemote}->command_write($strCommand); # Run it remotely - my $oSSH = $self->remote_get($strPathType); - my $strOutput = $oSSH->capture($strCommand); + my ($strOutput, $bError, $iErrorCode) = $self->{oRemote}->output_read(); # Capture any errors - if ($oSSH->error) + if ($bError) { - confess &log(ERROR, "${strErrorPrefix} remote (${strCommand}): " . (defined($strOutput) ? $strOutput : $oSSH->error)); + confess &log(ERROR, "${strErrorPrefix} remote (${strCommand}): " . $strOutput); } $bExists = $strOutput eq "Y"; @@ -1156,9 +1156,19 @@ sub exists # Run locally else { - if (-e $strPathOp) + # Stat the file/path to determine if it exists + my $oStat = lstat($strPathOp); + + # Evaluate error + if (!defined($oStat)) { - $bExists = true; + # If the error is not entry missing, then throw error + if (!$!{ENOENT}) + { + confess &log(ERROR, $!, COMMAND_ERR_FILE_READ); + } + + $bExists = false; } } diff --git a/pg_backrest_remote.pm b/pg_backrest_remote.pm index 9f357370e..4cb302b18 100644 --- a/pg_backrest_remote.pm +++ b/pg_backrest_remote.pm @@ -13,10 +13,12 @@ use Net::OpenSSH; use File::Basename; use lib dirname($0); +use lib dirname($0) . "/../lib"; +use BackRest::Exception; use pg_backrest_utility; # Protocol strings -has strGreeting => (is => 'ro', default => 'pg_backrest_remote 0.20'); +has strGreeting => (is => 'ro', default => 'PG_BACKREST_REMOTE 0.20'); # Command strings has strCommand => (is => 'bare'); @@ -96,7 +98,6 @@ sub clone sub greeting_read { my $self = shift; - my $hOut = shift; # Make sure that the remote is running the right version if (trim(readline($self->{hOut})) ne $self->{strGreeting}) @@ -111,7 +112,6 @@ sub greeting_read sub greeting_write { my $self = shift; - my $hOut = shift; if (!syswrite(*STDOUT, "$self->{strGreeting}\n")) { @@ -119,5 +119,151 @@ sub greeting_write } } +#################################################################################################################################### +# STRING_WRITE +#################################################################################################################################### +sub string_write +{ + my $self = shift; + my $hOut = shift; + my $strBuffer = shift; + + $strBuffer =~ s/\n/\n\./g; + + if (!syswrite($hOut, "." . $strBuffer)) + { + confess "unable to write string"; + } +} + +#################################################################################################################################### +# ERROR_WRITE +#################################################################################################################################### +sub error_write +{ + my $self = shift; + my $oMessage = shift; + + my $iCode; + my $strMessage; + + if (blessed($oMessage)) + { + if ($oMessage->isa("BackRest::Exception")) + { + $iCode = $oMessage->code(); + $strMessage = $oMessage->message(); + } + else + { + $strMessage = 'unknown error object'; + } + } + else + { + $strMessage = $oMessage; + } + + if (defined($strMessage)) + { + $self->string_write(*STDOUT, trim($strMessage)); + } + + if (!syswrite(*STDOUT, "\nERROR" . (defined($iCode) ? " $iCode" : "") . "\n")) + { + confess "unable to write error"; + } +} + +#################################################################################################################################### +# OUTPUT_READ +#################################################################################################################################### +sub output_read +{ + my $self = shift; + + my $strLine; + my $strOutput; + my $bError = false; + my $iErrorCode; + + while ($strLine = readline($self->{hOut})) + { + if ($strLine =~ /^ERROR.*/) + { + $bError = true; + last; + } + + if (trim($strLine) =~ /^OK$/) + { + last; + } + + $strOutput .= trim(substr($strLine, 1)); + } + + return ($strOutput, $bError, $iErrorCode); +} + +#################################################################################################################################### +# OUTPUT_WRITE +#################################################################################################################################### +sub output_write +{ + my $self = shift; + my $strOutput = shift; + + $self->string_write(*STDOUT, $strOutput); + + if (!syswrite(*STDOUT, "\nOK\n")) + { + confess "unable to write output"; + } +} + +#################################################################################################################################### +# COMMAND_READ +#################################################################################################################################### +sub command_read +{ + my $self = shift; + + my $strOut = readline(*STDIN); + my $iPos = index($strOut, ':'); + + my $strCommand; + my $strOptions; + + # If no colon then there are no options + if ($iPos == -1) + { + $strCommand = lc(trim($strOut)); + } + # Else parse options + else + { + $strCommand = lc(substr($strOut, 0, $iPos)); + $strOptions = trim(substr($strOut, $iPos + 1)); + } + + return $strCommand, $strOptions; +} + +#################################################################################################################################### +# COMMAND_WRITE +#################################################################################################################################### +sub command_write +{ + my $self = shift; + my $strCommand = shift; + my $strOptions = shift; + + if (!syswrite($self->{hIn}, "$strCommand" . (defined($strOptions) ? ":${strOptions}" : "") . "\n")) + { + confess "unable to write command"; + } +} + no Moose; __PACKAGE__->meta->make_immutable; diff --git a/pg_backrest_utility.pm b/pg_backrest_utility.pm index a683168d6..a4f888283 100644 --- a/pg_backrest_utility.pm +++ b/pg_backrest_utility.pm @@ -11,6 +11,10 @@ use Carp; use IPC::System::Simple qw(capture); use Fcntl qw(:DEFAULT :flock); use File::Path qw(remove_tree); +use File::Basename; + +use lib dirname($0) . "/../lib"; +use BackRest::Exception; use Exporter qw(import); @@ -319,6 +323,9 @@ sub log { my $strLevel = shift; my $strMessage = shift; + my $iCode = shift; + + my $strMessageFormat = $strMessage; if (!defined($oLogLevelRank{"${strLevel}"}{rank})) { @@ -327,37 +334,42 @@ sub log my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time); - if (!defined($strMessage)) + if (!defined($strMessageFormat)) { - $strMessage = "(undefined)"; + $strMessageFormat = "(undefined)"; } if ($strLevel eq "TRACE") { - $strMessage = " " . $strMessage; + $strMessageFormat = " " . $strMessageFormat; } elsif ($strLevel eq "DEBUG") { - $strMessage = " " . $strMessage; + $strMessageFormat = " " . $strMessageFormat; } - $strMessage = sprintf("%4d-%02d-%02d %02d:%02d:%02d", $year+1900, $mon+1, $mday, $hour, $min, $sec) . - (" " x (7 - length($strLevel))) . "${strLevel} " . (" " x (2 - length(threads->tid()))) . - threads->tid() . ": ${strMessage}\n"; + $strMessageFormat = sprintf("%4d-%02d-%02d %02d:%02d:%02d", $year+1900, $mon+1, $mday, $hour, $min, $sec) . + (" " x (7 - length($strLevel))) . "${strLevel} " . (" " x (2 - length(threads->tid()))) . + threads->tid() . ": ${strMessageFormat}\n"; if ($oLogLevelRank{"${strLevel}"}{rank} <= $oLogLevelRank{"${strLogLevelConsole}"}{rank}) { - print $strMessage; + print $strMessageFormat; } if ($oLogLevelRank{"${strLevel}"}{rank} <= $oLogLevelRank{"${strLogLevelFile}"}{rank}) { if (defined($hLogFile)) { - print $hLogFile $strMessage; + print $hLogFile $strMessageFormat; } } + if (defined($iCode)) + { + return BackRest::Exception->new(iCode => $iCode, strMessage => $strMessage); + } + return $strMessage; }