#!/usr/bin/perl #################################################################################################################################### # pgBackRest - Simple PostgreSQL Backup and Restore #################################################################################################################################### #################################################################################################################################### # Perl includes #################################################################################################################################### use strict; use warnings FATAL => qw(all); use Carp qw(confess); # Convert die to confess to capture the stack trace $SIG{__DIE__} = sub { Carp::confess @_ }; use File::Basename qw(dirname); use Scalar::Util qw(blessed); use lib dirname($0) . '/../lib'; use BackRest::Archive; use BackRest::Common::Exception; use BackRest::Common::Log; use BackRest::Config::Config; use BackRest::File; #################################################################################################################################### # Operation constants #################################################################################################################################### use constant OP_MAIN => 'Main'; use constant OP_MAIN_SAFE_EXIT => OP_MAIN . '::safeExit'; #################################################################################################################################### # safeExit # # Terminate all threads and SSH connections when the script is terminated. #################################################################################################################################### my $iThreadMax = 1; sub safeExit { # Assign function parameters, defaults, and log debug info my ( $strOperation, $iExitCode ) = logDebugParam ( OP_MAIN_SAFE_EXIT, \@_, {name => 'iExitCode', required => false} ); commandStop(); # Stop threads if threading is enabled my $iThreadsStopped = 0; if ($iThreadMax > 1) { &log(DEBUG, "stop ${iThreadMax} threads"); # Don't fail if the threads cannot be stopped eval { $iThreadsStopped = threadGroupDestroy(); }; if ($@ && defined($iExitCode)) { &log(WARN, "unable to stop threads: $@"); } } # Don't fail if protocol cannot be destroyed eval { protocolDestroy(); }; if ($@ && defined($iExitCode)) { &log(WARN, "unable to shutdown protocol: $@"); } # Exit with code when defined if (defined($iExitCode)) { exit $iExitCode; } # Else the process was terminated on a signal or exception &log(ERROR, 'process terminated on signal or exception' . ($iThreadsStopped > 0 ? "${iThreadsStopped} threads stopped" : '')); } $SIG{TERM} = \&safeExit; $SIG{HUP} = \&safeExit; $SIG{INT} = \&safeExit; #################################################################################################################################### # START EVAL BLOCK TO CATCH ERRORS AND STOP THREADS #################################################################################################################################### eval { ################################################################################################################################ # Load command line parameters and config ################################################################################################################################ my $bConfigResult = configLoad(); # Display help and version if (commandTest(CMD_HELP) || commandTest(CMD_VERSION)) { # Load module dynamically require BackRest::Config::ConfigHelp; BackRest::Config::ConfigHelp->import(); # Generate help and exit configHelp($ARGV[1], $ARGV[2], commandTest(CMD_VERSION), $bConfigResult); safeExit(0); } ################################################################################################################################ # Process remote commands ################################################################################################################################ if (commandTest(CMD_REMOTE)) { # Turn all logging off logLevelSet(OFF, OFF); # Load module dynamically require BackRest::Protocol::RemoteMinion; BackRest::Protocol::RemoteMinion->import(); # Create the remote object my $oRemote = new BackRest::Protocol::RemoteMinion ( optionGet(OPTION_BUFFER_SIZE), optionGet(OPTION_COMPRESS_LEVEL), optionGet(OPTION_COMPRESS_LEVEL_NETWORK) ); # Process remote requests safeExit($oRemote->process()); } # Set the log levels logLevelSet(optionGet(OPTION_LOG_LEVEL_FILE), optionGet(OPTION_LOG_LEVEL_CONSOLE)); # Set test options !optionGet(OPTION_TEST) or testSet(optionGet(OPTION_TEST), optionGet(OPTION_TEST_DELAY)); # Log the command start commandStart(); ################################################################################################################################ # Process archive commands ################################################################################################################################ if (commandTest(CMD_ARCHIVE_PUSH) || commandTest(CMD_ARCHIVE_GET)) { safeExit(new BackRest::Archive()->process()); } ################################################################################################################################ # Process info command ################################################################################################################################ if (commandTest(CMD_INFO)) { # Load module dynamically require BackRest::Info; BackRest::Info->import(); safeExit(new BackRest::Info()->process()); } ################################################################################################################################ # Acquire the command lock ################################################################################################################################ # Load module dynamically require BackRest::Common::Lock; BackRest::Common::Lock->import(); lockAcquire(commandGet()); ################################################################################################################################ # Open the log file ################################################################################################################################ logFileSet(optionGet(OPTION_REPO_PATH) . '/log/' . optionGet(OPTION_STANZA) . '-' . lc(commandGet())); ################################################################################################################################ # Create the thread group that will be used for parallel processing ################################################################################################################################ if (optionTest(OPTION_THREAD_MAX) && optionGet(OPTION_THREAD_MAX) > 1) { # Set local thread-max so safeExit knows to stop them on exit $iThreadMax = optionGet(OPTION_THREAD_MAX); # Load module dynamically require BackRest::Protocol::ThreadGroup; BackRest::Protocol::ThreadGroup->import(); threadGroupCreate(); } ################################################################################################################################ # Initialize the default file object ################################################################################################################################ my $oFile = new BackRest::File ( optionGet(OPTION_STANZA), optionRemoteTypeTest(BACKUP) ? optionGet(OPTION_REPO_REMOTE_PATH) : optionGet(OPTION_REPO_PATH), optionRemoteType(), protocolGet() ); ################################################################################################################################ # RESTORE ################################################################################################################################ if (commandTest(CMD_RESTORE)) { if (optionRemoteTypeTest(DB)) { confess &log(ASSERT, 'restore command must be performed locally on the db server'); } # Load module dynamically require BackRest::Restore; BackRest::Restore->import(); # Do the restore new BackRest::Restore ( $oFile )->process; safeExit(0); } ################################################################################################################################ # Make sure backup and expire command happen on the backup side ################################################################################################################################ if (optionRemoteTypeTest(BACKUP)) { confess &log(ERROR, 'backup and expire commands must run on the backup host'); } ################################################################################################################################ # BACKUP ################################################################################################################################ if (commandTest(CMD_BACKUP)) { # Load module dynamically require BackRest::Backup; BackRest::Backup->import(); new BackRest::Backup ( $oFile )->process(); commandSet(CMD_EXPIRE); } ################################################################################################################################ # EXPIRE ################################################################################################################################ if (commandTest(CMD_EXPIRE)) { # Load module dynamically require BackRest::Expire; BackRest::Expire->import(); new BackRest::Expire ( $oFile )->process(); } # Release the command lock lockRelease(); safeExit(0); }; #################################################################################################################################### # CHECK FOR ERRORS AND STOP THREADS #################################################################################################################################### if ($@) { my $oMessage = $@; # If a backrest exception then return the code - don't confess if (blessed($oMessage) && $oMessage->isa('BackRest::Common::Exception')) { safeExit($oMessage->code()); } safeExit(); confess $oMessage; }