You've already forked pgbackrest
mirror of
https://github.com/pgbackrest/pgbackrest.git
synced 2026-05-22 10:15:16 +02:00
e4cc008b98
At some point this hint got added to the underlying code so it is no longer needed here.
19746 lines
744 KiB
C
19746 lines
744 KiB
C
/***********************************************************************************************************************************
|
|
Embed Perl modules
|
|
|
|
Automatically generated by Build.pm -- do not modify directly.
|
|
***********************************************************************************************************************************/
|
|
|
|
/***********************************************************************************************************************************
|
|
Embedded Perl modules
|
|
***********************************************************************************************************************************/
|
|
static const EmbeddedModule embeddedModule[] =
|
|
{
|
|
{
|
|
.name = "pgBackRest/Archive/Common.pm",
|
|
.data =
|
|
"\n\n\n"
|
|
"package pgBackRest::Archive::Common;\n"
|
|
"\n"
|
|
"use strict;\n"
|
|
"use warnings FATAL => qw(all);\n"
|
|
"use Carp qw(confess);\n"
|
|
"\n"
|
|
"use Config;\n"
|
|
"use Exporter qw(import);\n"
|
|
"our @EXPORT = qw();\n"
|
|
"use Fcntl qw(SEEK_CUR O_RDONLY);\n"
|
|
"use File::Basename qw(dirname);\n"
|
|
"\n"
|
|
"use pgBackRest::Db;\n"
|
|
"use pgBackRest::DbVersion;\n"
|
|
"use pgBackRest::Common::Exception;\n"
|
|
"use pgBackRest::Common::Log;\n"
|
|
"use pgBackRest::Common::Wait;\n"
|
|
"use pgBackRest::Config::Config;\n"
|
|
"use pgBackRest::Protocol::Storage::Helper;\n"
|
|
"use pgBackRest::Storage::Helper;\n"
|
|
"\n\n\n\n"
|
|
"use constant REGEX_ARCHIVE_DIR_DB_VERSION => '^[0-9]+(\\.[0-9]+)*-[0-9]+$';\n"
|
|
"push @EXPORT, qw(REGEX_ARCHIVE_DIR_DB_VERSION);\n"
|
|
"use constant REGEX_ARCHIVE_DIR_WAL => '^[0-F]{16}$';\n"
|
|
"push @EXPORT, qw(REGEX_ARCHIVE_DIR_WAL);\n"
|
|
"\n\n\n\n"
|
|
"use constant PG_WAL_SYSTEM_ID_OFFSET_GTE_93 => 12 + $Config{ptrsize};\n"
|
|
"push @EXPORT, qw(PG_WAL_SYSTEM_ID_OFFSET_GTE_93);\n"
|
|
"use constant PG_WAL_SYSTEM_ID_OFFSET_LT_93 => 12;\n"
|
|
"push @EXPORT, qw(PG_WAL_SYSTEM_ID_OFFSET_LT_93);\n"
|
|
"\n\n\n\n"
|
|
"use constant PG_WAL_SEGMENT_SIZE => 16777216;\n"
|
|
"push @EXPORT, qw(PG_WAL_SEGMENT_SIZE);\n"
|
|
"\n\n\n\n\n\n"
|
|
"sub lsnNormalize\n"
|
|
"{\n"
|
|
"\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$strLsn,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '::lsnFile', \\@_,\n"
|
|
"{name => 'strLsn', trace => true},\n"
|
|
");\n"
|
|
"\n\n"
|
|
"my @stryLsnSplit = split('/', $strLsn);\n"
|
|
"\n"
|
|
"if (@stryLsnSplit != 2)\n"
|
|
"{\n"
|
|
"confess &log(ASSERT, \"invalid lsn ${strLsn}\");\n"
|
|
"}\n"
|
|
"\n"
|
|
"my $strLsnNormal = uc(sprintf(\"%08s%08s\", $stryLsnSplit[0], $stryLsnSplit[1]));\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'strLsnNormal', value => $strLsnNormal, trace => true}\n"
|
|
");\n"
|
|
"\n"
|
|
"}\n"
|
|
"\n"
|
|
"push @EXPORT, qw(lsnNormalize);\n"
|
|
"\n\n\n\n\n\n\n"
|
|
"sub lsnFileRange\n"
|
|
"{\n"
|
|
"\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$strLsnStart,\n"
|
|
"$strLsnStop,\n"
|
|
"$strDbVersion,\n"
|
|
"$iWalSegmentSize,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '::lsnFileRange', \\@_,\n"
|
|
"{name => 'strLsnStart'},\n"
|
|
"{name => 'strLsnStop'},\n"
|
|
"{name => '$strDbVersion'},\n"
|
|
"{name => '$iWalSegmentSize'},\n"
|
|
");\n"
|
|
"\n\n"
|
|
"my @stryArchive;\n"
|
|
"my $iArchiveIdx = 0;\n"
|
|
"my $bSkipFF = $strDbVersion < PG_VERSION_93;\n"
|
|
"\n\n"
|
|
"my @stryArchiveSplit = split('/', $strLsnStart);\n"
|
|
"my $iStartMajor = hex($stryArchiveSplit[0]);\n"
|
|
"my $iStartMinor = int(hex($stryArchiveSplit[1]) / $iWalSegmentSize);\n"
|
|
"\n"
|
|
"@stryArchiveSplit = split('/', $strLsnStop);\n"
|
|
"my $iStopMajor = hex($stryArchiveSplit[0]);\n"
|
|
"my $iStopMinor = int(hex($stryArchiveSplit[1]) / $iWalSegmentSize);\n"
|
|
"\n"
|
|
"$stryArchive[$iArchiveIdx] = uc(sprintf(\"%08x%08x\", $iStartMajor, $iStartMinor));\n"
|
|
"$iArchiveIdx += 1;\n"
|
|
"\n"
|
|
"while (!($iStartMajor == $iStopMajor && $iStartMinor == $iStopMinor))\n"
|
|
"{\n"
|
|
"$iStartMinor += 1;\n"
|
|
"\n"
|
|
"if ($bSkipFF && $iStartMinor == 255 || !$bSkipFF && $iStartMinor > int(0xFFFFFFFF / $iWalSegmentSize))\n"
|
|
"{\n"
|
|
"$iStartMajor += 1;\n"
|
|
"$iStartMinor = 0;\n"
|
|
"}\n"
|
|
"\n"
|
|
"$stryArchive[$iArchiveIdx] = uc(sprintf(\"%08x%08x\", $iStartMajor, $iStartMinor));\n"
|
|
"$iArchiveIdx += 1;\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'stryWalFileName', value => \\@stryArchive}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n"
|
|
"push @EXPORT, qw(lsnFileRange);\n"
|
|
"\n\n\n\n\n\n\n"
|
|
"sub walSegmentFind\n"
|
|
"{\n"
|
|
"\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$oStorageRepo,\n"
|
|
"$strArchiveId,\n"
|
|
"$strWalSegment,\n"
|
|
"$iWaitSeconds,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '::walSegmentFind', \\@_,\n"
|
|
"{name => 'oStorageRepo'},\n"
|
|
"{name => 'strArchiveId'},\n"
|
|
"{name => 'strWalSegment'},\n"
|
|
"{name => 'iWaitSeconds', required => false},\n"
|
|
");\n"
|
|
"\n\n"
|
|
"if (!walIsSegment($strWalSegment))\n"
|
|
"{\n"
|
|
"confess &log(ERROR, \"${strWalSegment} is not a WAL segment\", ERROR_ASSERT);\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"my $oWait = waitInit($iWaitSeconds);\n"
|
|
"my @stryWalFileName;\n"
|
|
"\n"
|
|
"do\n"
|
|
"{\n"
|
|
"\n"
|
|
"push(@stryWalFileName, $oStorageRepo->list(\n"
|
|
"STORAGE_REPO_ARCHIVE . \"/${strArchiveId}/\" . substr($strWalSegment, 0, 16),\n"
|
|
"{strExpression =>\n"
|
|
"'^' . substr($strWalSegment, 0, 24) . (walIsPartial($strWalSegment) ? \"\\\\.partial\" : '') .\n"
|
|
"\"-[0-f]{40}(\\\\.\" . COMPRESS_EXT . \"){0,1}\\$\",\n"
|
|
"bIgnoreMissing => true}));\n"
|
|
"}\n"
|
|
"while (@stryWalFileName == 0 && waitMore($oWait));\n"
|
|
"\n\n\n"
|
|
"if (@stryWalFileName > 1)\n"
|
|
"{\n"
|
|
"confess &log(ERROR,\n"
|
|
"\"duplicates found in archive for WAL segment ${strWalSegment}: \" . join(', ', @stryWalFileName) .\n"
|
|
"\"\\nHINT: are multiple primaries archiving to this stanza?\",\n"
|
|
"ERROR_ARCHIVE_DUPLICATE);\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if (@stryWalFileName == 0 && defined($iWaitSeconds))\n"
|
|
"{\n"
|
|
"confess &log(\n"
|
|
"ERROR,\n"
|
|
"\"could not find WAL segment ${strWalSegment} after ${iWaitSeconds} second(s)\" .\n"
|
|
"\"\\nHINT: is archive_command configured correctly?\" .\n"
|
|
"\"\\nHINT: use the check command to verify that PostgreSQL is archiving.\",\n"
|
|
"ERROR_ARCHIVE_TIMEOUT);\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'strWalFileName', value => $stryWalFileName[0]}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n"
|
|
"push @EXPORT, qw(walSegmentFind);\n"
|
|
"\n\n\n\n\n\n"
|
|
"sub walIsSegment\n"
|
|
"{\n"
|
|
"\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$strWalFile,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '::walIsSegment', \\@_,\n"
|
|
"{name => 'strWalFile', trace => true},\n"
|
|
");\n"
|
|
"\n"
|
|
"return $strWalFile =~ /^[0-F]{24}(\\.partial){0,1}$/ ? true : false;\n"
|
|
"}\n"
|
|
"\n"
|
|
"push @EXPORT, qw(walIsSegment);\n"
|
|
"\n\n\n\n\n\n"
|
|
"sub walIsPartial\n"
|
|
"{\n"
|
|
"\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$strWalFile,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '::walIsPartial', \\@_,\n"
|
|
"{name => 'strWalFile', trace => true},\n"
|
|
");\n"
|
|
"\n"
|
|
"return walIsSegment($strWalFile) && $strWalFile =~ /\\.partial$/ ? true : false;\n"
|
|
"}\n"
|
|
"\n"
|
|
"push @EXPORT, qw(walIsPartial);\n"
|
|
"\n"
|
|
"1;\n"
|
|
},
|
|
{
|
|
.name = "pgBackRest/Archive/Get/File.pm",
|
|
.data =
|
|
"\n\n\n"
|
|
"package pgBackRest::Archive::Get::File;\n"
|
|
"\n"
|
|
"use strict;\n"
|
|
"use warnings FATAL => qw(all);\n"
|
|
"use Carp qw(confess);\n"
|
|
"use English '-no_match_vars';\n"
|
|
"\n"
|
|
"use Exporter qw(import);\n"
|
|
"our @EXPORT = qw();\n"
|
|
"use File::Basename qw(basename dirname);\n"
|
|
"\n"
|
|
"use pgBackRest::Archive::Common;\n"
|
|
"use pgBackRest::Archive::Info;\n"
|
|
"use pgBackRest::Db;\n"
|
|
"use pgBackRest::Common::Exception;\n"
|
|
"use pgBackRest::Common::Lock;\n"
|
|
"use pgBackRest::Common::Log;\n"
|
|
"use pgBackRest::Config::Config;\n"
|
|
"use pgBackRest::Protocol::Helper;\n"
|
|
"use pgBackRest::Protocol::Storage::Helper;\n"
|
|
"use pgBackRest::Storage::Base;\n"
|
|
"use pgBackRest::Storage::Filter::Gzip;\n"
|
|
"use pgBackRest::Storage::Filter::Sha;\n"
|
|
"use pgBackRest::Storage::Helper;\n"
|
|
"\n\n\n\n\n"
|
|
"sub archiveGetCheck\n"
|
|
"{\n"
|
|
"\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$strDbVersion,\n"
|
|
"$ullDbSysId,\n"
|
|
"$strFile,\n"
|
|
"$bCheck,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '::archiveGetCheck', \\@_,\n"
|
|
"{name => 'strDbVersion', required => false},\n"
|
|
"{name => 'ullDbSysId', required => false},\n"
|
|
"{name => 'strFile', required => false},\n"
|
|
"{name => 'bCheck', required => false, default => true},\n"
|
|
");\n"
|
|
"\n"
|
|
"my @stryArchiveId = ();\n"
|
|
"my $strArchiveId;\n"
|
|
"my $strArchiveFile;\n"
|
|
"my $strCipherPass;\n"
|
|
"\n\n"
|
|
"if (!defined($strDbVersion) || !defined($ullDbSysId) )\n"
|
|
"{\n"
|
|
"\n"
|
|
"($strDbVersion, my $iControlVersion, my $iCatalogVersion, $ullDbSysId) = dbMasterGet()->info();\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if (!isRepoLocal())\n"
|
|
"{\n"
|
|
"($strArchiveId, $strArchiveFile, $strCipherPass) = protocolGet(CFGOPTVAL_REMOTE_TYPE_BACKUP)->cmdExecute(\n"
|
|
"OP_ARCHIVE_GET_CHECK, [$strDbVersion, $ullDbSysId, $strFile, $bCheck], true);\n"
|
|
"}\n"
|
|
"else\n"
|
|
"{\n"
|
|
"my $oArchiveInfo = new pgBackRest::Archive::Info(storageRepo()->pathGet(STORAGE_REPO_ARCHIVE), true);\n"
|
|
"\n\n"
|
|
"if ($bCheck)\n"
|
|
"{\n"
|
|
"push(@stryArchiveId, $oArchiveInfo->check($strDbVersion, $ullDbSysId));\n"
|
|
"}\n"
|
|
"\n"
|
|
"else\n"
|
|
"{\n"
|
|
"@stryArchiveId = $oArchiveInfo->archiveIdList($strDbVersion, $ullDbSysId);\n"
|
|
"}\n"
|
|
"\n\n\n"
|
|
"$strArchiveId = $stryArchiveId[0];\n"
|
|
"\n\n"
|
|
"if (defined($strFile))\n"
|
|
"{\n"
|
|
"foreach my $strId (@stryArchiveId)\n"
|
|
"{\n"
|
|
"\n"
|
|
"if (walIsSegment($strFile))\n"
|
|
"{\n"
|
|
"$strArchiveFile = walSegmentFind(storageRepo(), $strId, $strFile);\n"
|
|
"}\n"
|
|
"\n"
|
|
"elsif (storageRepo()->exists(STORAGE_REPO_ARCHIVE . \"/${strId}/${strFile}\"))\n"
|
|
"{\n"
|
|
"$strArchiveFile = $strFile;\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if (defined($strArchiveFile))\n"
|
|
"{\n"
|
|
"$strArchiveId = $strId;\n"
|
|
"last;\n"
|
|
"}\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"$strCipherPass = $oArchiveInfo->cipherPassSub();\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'strArchiveId', value => $strArchiveId},\n"
|
|
"{name => 'strArchiveFile', value => $strArchiveFile},\n"
|
|
"{name => 'strCipherPass', value => $strCipherPass, redact => true}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n"
|
|
"push @EXPORT, qw(archiveGetCheck);\n"
|
|
"\n"
|
|
"1;\n"
|
|
},
|
|
{
|
|
.name = "pgBackRest/Archive/Info.pm",
|
|
.data =
|
|
"\n\n\n\n\n\n\n"
|
|
"package pgBackRest::Archive::Info;\n"
|
|
"use parent 'pgBackRest::Common::Ini';\n"
|
|
"\n"
|
|
"use strict;\n"
|
|
"use warnings FATAL => qw(all);\n"
|
|
"use Carp qw(confess);\n"
|
|
"use English '-no_match_vars';\n"
|
|
"\n"
|
|
"use Exporter qw(import);\n"
|
|
"our @EXPORT = qw();\n"
|
|
"use File::Basename qw(dirname basename);\n"
|
|
"\n"
|
|
"use pgBackRest::Archive::Common;\n"
|
|
"use pgBackRest::Common::Exception;\n"
|
|
"use pgBackRest::Config::Config;\n"
|
|
"use pgBackRest::Common::Ini;\n"
|
|
"use pgBackRest::Common::Log;\n"
|
|
"use pgBackRest::DbVersion;\n"
|
|
"use pgBackRest::InfoCommon;\n"
|
|
"use pgBackRest::Manifest;\n"
|
|
"use pgBackRest::Protocol::Storage::Helper;\n"
|
|
"use pgBackRest::Storage::Base;\n"
|
|
"use pgBackRest::Storage::Filter::Gzip;\n"
|
|
"use pgBackRest::Storage::Helper;\n"
|
|
"\n\n\n\n"
|
|
"use constant ARCHIVE_INFO_FILE => 'archive.info';\n"
|
|
"push @EXPORT, qw(ARCHIVE_INFO_FILE);\n"
|
|
"\n\n\n\n"
|
|
"use constant INFO_ARCHIVE_SECTION_DB => INFO_BACKUP_SECTION_DB;\n"
|
|
"push @EXPORT, qw(INFO_ARCHIVE_SECTION_DB);\n"
|
|
"use constant INFO_ARCHIVE_SECTION_DB_HISTORY => INFO_BACKUP_SECTION_DB_HISTORY;\n"
|
|
"push @EXPORT, qw(INFO_ARCHIVE_SECTION_DB_HISTORY);\n"
|
|
"\n"
|
|
"use constant INFO_ARCHIVE_KEY_DB_VERSION => MANIFEST_KEY_DB_VERSION;\n"
|
|
"push @EXPORT, qw(INFO_ARCHIVE_KEY_DB_VERSION);\n"
|
|
"use constant INFO_ARCHIVE_KEY_DB_ID => MANIFEST_KEY_DB_ID;\n"
|
|
"push @EXPORT, qw(INFO_ARCHIVE_KEY_DB_ID);\n"
|
|
"use constant INFO_ARCHIVE_KEY_DB_SYSTEM_ID => MANIFEST_KEY_SYSTEM_ID;\n"
|
|
"push @EXPORT, qw(INFO_ARCHIVE_KEY_DB_SYSTEM_ID);\n"
|
|
"\n\n\n\n"
|
|
"my $strArchiveInfoMissingMsg =\n"
|
|
"ARCHIVE_INFO_FILE . \" does not exist but is required to push/get WAL segments\\n\" .\n"
|
|
"\"HINT: is archive_command configured in postgresql.conf?\\n\" .\n"
|
|
"\"HINT: has a stanza-create been performed?\\n\" .\n"
|
|
"\"HINT: use --no-archive-check to disable archive checks during backup if you have an alternate archiving scheme.\";\n"
|
|
"\n\n\n\n"
|
|
"sub new\n"
|
|
"{\n"
|
|
"my $class = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$strArchiveClusterPath,\n"
|
|
"$bRequired,\n"
|
|
"$bLoad,\n"
|
|
"$bIgnoreMissing, # Don't error on missing files\n"
|
|
"$strCipherPassSub,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->new', \\@_,\n"
|
|
"{name => 'strArchiveClusterPath'},\n"
|
|
"{name => 'bRequired', default => true},\n"
|
|
"{name => 'bLoad', optional => true, default => true},\n"
|
|
"{name => 'bIgnoreMissing', optional => true, default => false},\n"
|
|
"{name => 'strCipherPassSub', optional => true},\n"
|
|
");\n"
|
|
"\n\n"
|
|
"my $strArchiveInfoFile = \"${strArchiveClusterPath}/\" . ARCHIVE_INFO_FILE;\n"
|
|
"my $self = {};\n"
|
|
"my $iResult = 0;\n"
|
|
"my $strResultMessage;\n"
|
|
"\n\n"
|
|
"eval\n"
|
|
"{\n"
|
|
"$self = $class->SUPER::new($strArchiveInfoFile, {bLoad => $bLoad, bIgnoreMissing => $bIgnoreMissing,\n"
|
|
"oStorage => storageRepo(), strCipherPass => storageRepo()->cipherPassUser(),\n"
|
|
"strCipherPassSub => $strCipherPassSub});\n"
|
|
"return true;\n"
|
|
"}\n"
|
|
"or do\n"
|
|
"{\n"
|
|
"\n"
|
|
"$iResult = exceptionCode($EVAL_ERROR);\n"
|
|
"$strResultMessage = exceptionMessage($EVAL_ERROR);\n"
|
|
"};\n"
|
|
"\n"
|
|
"if ($iResult != 0)\n"
|
|
"{\n"
|
|
"\n\n"
|
|
"if ($iResult == ERROR_FILE_MISSING)\n"
|
|
"{\n"
|
|
"if ($bRequired)\n"
|
|
"{\n"
|
|
"confess &log(ERROR, $strArchiveInfoMissingMsg, ERROR_FILE_MISSING);\n"
|
|
"}\n"
|
|
"}\n"
|
|
"elsif ($iResult == ERROR_CRYPTO && $strResultMessage =~ \"^unable to flush\")\n"
|
|
"{\n"
|
|
"confess &log(ERROR, \"unable to parse '$strArchiveInfoFile'\\nHINT: Is or was the repo encrypted?\", $iResult);\n"
|
|
"}\n"
|
|
"else\n"
|
|
"{\n"
|
|
"confess $EVAL_ERROR;\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n"
|
|
"$self->{strArchiveClusterPath} = $strArchiveClusterPath;\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'self', value => $self}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n\n\n\n"
|
|
"sub check\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$strDbVersion,\n"
|
|
"$ullDbSysId,\n"
|
|
"$bRequired,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->check', \\@_,\n"
|
|
"{name => 'strDbVersion'},\n"
|
|
"{name => 'ullDbSysId'},\n"
|
|
"{name => 'bRequired', default => true},\n"
|
|
");\n"
|
|
"\n\n"
|
|
"if ($bRequired)\n"
|
|
"{\n"
|
|
"\n"
|
|
"$self->confirmExists();\n"
|
|
"}\n"
|
|
"\n"
|
|
"my $strError = undef;\n"
|
|
"\n"
|
|
"if (!$self->test(INFO_ARCHIVE_SECTION_DB, INFO_ARCHIVE_KEY_DB_VERSION, undef, $strDbVersion))\n"
|
|
"{\n"
|
|
"$strError = \"WAL segment version ${strDbVersion} does not match archive version \" .\n"
|
|
"$self->get(INFO_ARCHIVE_SECTION_DB, INFO_ARCHIVE_KEY_DB_VERSION);\n"
|
|
"}\n"
|
|
"\n"
|
|
"if (!$self->test(INFO_ARCHIVE_SECTION_DB, INFO_ARCHIVE_KEY_DB_SYSTEM_ID, undef, $ullDbSysId))\n"
|
|
"{\n"
|
|
"$strError = (defined($strError) ? ($strError . \"\\n\") : \"\") .\n"
|
|
"\"WAL segment system-id ${ullDbSysId} does not match archive system-id \" .\n"
|
|
"$self->get(INFO_ARCHIVE_SECTION_DB, INFO_ARCHIVE_KEY_DB_SYSTEM_ID);\n"
|
|
"}\n"
|
|
"\n"
|
|
"if (defined($strError))\n"
|
|
"{\n"
|
|
"confess &log(ERROR, \"${strError}\\nHINT: are you archiving to the correct stanza?\", ERROR_ARCHIVE_MISMATCH);\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'strArchiveId', value => $self->archiveId()}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n\n\n"
|
|
"sub archiveId\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$strDbVersion,\n"
|
|
"$ullDbSysId,\n"
|
|
") = logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->archiveId', \\@_,\n"
|
|
"{name => 'strDbVersion', optional => true},\n"
|
|
"{name => 'ullDbSysId', optional => true},\n"
|
|
");\n"
|
|
"\n"
|
|
"my $strArchiveId = undef;\n"
|
|
"\n\n"
|
|
"if (!defined($strDbVersion) && !defined($ullDbSysId))\n"
|
|
"{\n"
|
|
"$strArchiveId = $self->get(INFO_ARCHIVE_SECTION_DB, INFO_ARCHIVE_KEY_DB_VERSION) . \"-\" .\n"
|
|
"$self->get(INFO_ARCHIVE_SECTION_DB, INFO_ARCHIVE_KEY_DB_ID);\n"
|
|
"}\n"
|
|
"\n"
|
|
"elsif (defined($strDbVersion) && defined($ullDbSysId))\n"
|
|
"{\n"
|
|
"\n"
|
|
"$strArchiveId = ($self->archiveIdList($strDbVersion, $ullDbSysId))[0];\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'strArchiveId', value => $strArchiveId}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n\n\n"
|
|
"sub archiveIdList\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$strDbVersion,\n"
|
|
"$ullDbSysId,\n"
|
|
") = logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->archiveIdList', \\@_,\n"
|
|
"{name => 'strDbVersion'},\n"
|
|
"{name => 'ullDbSysId'},\n"
|
|
");\n"
|
|
"\n"
|
|
"my @stryArchiveId;\n"
|
|
"\n\n"
|
|
"my $hDbList = $self->dbHistoryList();\n"
|
|
"\n"
|
|
"foreach my $iDbHistoryId (sort {$a <=> $b} keys %$hDbList)\n"
|
|
"{\n"
|
|
"\n"
|
|
"if (($hDbList->{$iDbHistoryId}{&INFO_DB_VERSION} eq $strDbVersion) &&\n"
|
|
"($hDbList->{$iDbHistoryId}{&INFO_SYSTEM_ID} eq $ullDbSysId))\n"
|
|
"{\n"
|
|
"unshift(@stryArchiveId, $strDbVersion . \"-\" . $iDbHistoryId);\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if (@stryArchiveId == 0)\n"
|
|
"{\n"
|
|
"confess &log(\n"
|
|
"ERROR, \"unable to retrieve the archive id for database version '$strDbVersion' and system-id '$ullDbSysId'\",\n"
|
|
"ERROR_ARCHIVE_MISMATCH);\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'stryArchiveId', value => \\@stryArchiveId}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n\n\n"
|
|
"sub create\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$strDbVersion,\n"
|
|
"$ullDbSysId,\n"
|
|
"$bSave,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->create', \\@_,\n"
|
|
"{name => 'strDbVersion'},\n"
|
|
"{name => 'ullDbSysId'},\n"
|
|
"{name => 'bSave', default => true},\n"
|
|
");\n"
|
|
"\n\n"
|
|
"$self->dbSectionSet($strDbVersion, $ullDbSysId, $self->dbHistoryIdGet(false));\n"
|
|
"\n"
|
|
"if ($bSave)\n"
|
|
"{\n"
|
|
"$self->save();\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"return logDebugReturn($strOperation);\n"
|
|
"}\n"
|
|
"\n\n\n\n\n\n"
|
|
"sub reconstruct\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$strCurrentDbVersion,\n"
|
|
"$ullCurrentDbSysId,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->reconstruct', \\@_,\n"
|
|
"{name => 'strCurrentDbVersion'},\n"
|
|
"{name => 'ullCurrentDbSysId'},\n"
|
|
");\n"
|
|
"\n"
|
|
"my $strInvalidFileStructure = undef;\n"
|
|
"\n"
|
|
"my @stryArchiveId = storageRepo()->list(\n"
|
|
"$self->{strArchiveClusterPath}, {strExpression => REGEX_ARCHIVE_DIR_DB_VERSION, bIgnoreMissing => true});\n"
|
|
"my %hDbHistoryVersion;\n"
|
|
"\n\n"
|
|
"foreach my $strArchiveId (@stryArchiveId)\n"
|
|
"{\n"
|
|
"my ($strDbVersion, $iDbHistoryId) = split(\"-\", $strArchiveId);\n"
|
|
"$hDbHistoryVersion{$iDbHistoryId} = $strDbVersion;\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"foreach my $iDbHistoryId (sort {$a <=> $b} keys %hDbHistoryVersion)\n"
|
|
"{\n"
|
|
"my $strDbVersion = $hDbHistoryVersion{$iDbHistoryId};\n"
|
|
"my $strVersionDir = $strDbVersion . \"-\" . $iDbHistoryId;\n"
|
|
"\n\n"
|
|
"my $strArchiveDir = (storageRepo()->list(\n"
|
|
"$self->{strArchiveClusterPath} . \"/${strVersionDir}\",\n"
|
|
"{strExpression => REGEX_ARCHIVE_DIR_WAL, bIgnoreMissing => true}))[0];\n"
|
|
"\n\n"
|
|
"if (!defined($strArchiveDir))\n"
|
|
"{\n"
|
|
"$strInvalidFileStructure = \"found empty directory \" . $self->{strArchiveClusterPath} . \"/${strVersionDir}\";\n"
|
|
"next;\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"my $strArchiveFile = (storageRepo()->list(\n"
|
|
"$self->{strArchiveClusterPath} . \"/${strVersionDir}/${strArchiveDir}\",\n"
|
|
"{strExpression => \"^[0-F]{24}(\\\\.partial){0,1}(-[0-f]+){0,1}(\\\\.\" . COMPRESS_EXT . \"){0,1}\\$\",\n"
|
|
"bIgnoreMissing => true}))[0];\n"
|
|
"\n\n"
|
|
"if (!defined($strArchiveFile))\n"
|
|
"{\n"
|
|
"$strInvalidFileStructure =\n"
|
|
"\"found empty directory \" . $self->{strArchiveClusterPath} . \"/${strVersionDir}/${strArchiveDir}\";\n"
|
|
"next;\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"my $strArchiveFilePath = $self->{strArchiveClusterPath}.\"/${strVersionDir}/${strArchiveDir}/${strArchiveFile}\";\n"
|
|
"\n\n"
|
|
"my $iSysIdOffset = $strDbVersion >= PG_VERSION_93 ? PG_WAL_SYSTEM_ID_OFFSET_GTE_93 : PG_WAL_SYSTEM_ID_OFFSET_LT_93;\n"
|
|
"\n\n"
|
|
"my $tBlock;\n"
|
|
"\n\n"
|
|
"if (!storageRepo()->encryptionValid(storageRepo()->encrypted($strArchiveFilePath)))\n"
|
|
"{\n"
|
|
"confess &log(ERROR, \"encryption incompatible for '$strArchiveFilePath'\" .\n"
|
|
"\"\\nHINT: Is or was the repo encrypted?\", ERROR_CRYPTO);\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"my $oFileIo = storageRepo()->openRead(\n"
|
|
"$strArchiveFilePath,\n"
|
|
"{rhyFilter => $strArchiveFile =~ ('\\.' . COMPRESS_EXT . '$') ?\n"
|
|
"[{strClass => STORAGE_FILTER_GZIP, rxyParam => [{strCompressType => STORAGE_DECOMPRESS}]}] : undef,\n"
|
|
"strCipherPass => $self->cipherPassSub()});\n"
|
|
"\n"
|
|
"$oFileIo->read(\\$tBlock, 512, true);\n"
|
|
"$oFileIo->close();\n"
|
|
"\n\n"
|
|
"my ($iMagic, $iFlag, $junk, $ullDbSysId) = unpack('SSa' . $iSysIdOffset . 'Q', $tBlock);\n"
|
|
"\n"
|
|
"if (!defined($ullDbSysId))\n"
|
|
"{\n"
|
|
"confess &log(ERROR, \"unable to read database system identifier\", ERROR_FILE_READ);\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"$self->dbSectionSet($strDbVersion, $ullDbSysId, $iDbHistoryId);\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if (!$self->test(INFO_ARCHIVE_SECTION_DB))\n"
|
|
"{\n"
|
|
"$self->create($strCurrentDbVersion, $ullCurrentDbSysId, false);\n"
|
|
"}\n"
|
|
"\n"
|
|
"else\n"
|
|
"{\n"
|
|
"\n"
|
|
"logDisable();\n"
|
|
"\n"
|
|
"eval\n"
|
|
"{\n"
|
|
"$self->check($strCurrentDbVersion, $ullCurrentDbSysId, false);\n"
|
|
"logEnable();\n"
|
|
"return true;\n"
|
|
"}\n"
|
|
"or do\n"
|
|
"{\n"
|
|
"\n"
|
|
"logEnable();\n"
|
|
"\n\n"
|
|
"confess $EVAL_ERROR if (exceptionCode($EVAL_ERROR) != ERROR_ARCHIVE_MISMATCH);\n"
|
|
"\n\n"
|
|
"$self->dbSectionSet($strCurrentDbVersion, $ullCurrentDbSysId, $self->dbHistoryIdGet(false)+1);\n"
|
|
"};\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'strInvalidFileStructure', value => $strInvalidFileStructure}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n\n\n"
|
|
"sub dbHistoryIdGet\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$bFileRequired,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->dbHistoryIdGet', \\@_,\n"
|
|
"{name => 'bFileRequired', default => true},\n"
|
|
");\n"
|
|
"\n\n"
|
|
"if ($bFileRequired)\n"
|
|
"{\n"
|
|
"$self->confirmExists();\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"my $iDbHistoryId = (!$self->test(INFO_ARCHIVE_SECTION_DB))\n"
|
|
"? 1 : $self->numericGet(INFO_ARCHIVE_SECTION_DB, INFO_ARCHIVE_KEY_DB_ID);\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'iDbHistoryId', value => $iDbHistoryId}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n\n\n"
|
|
"sub dbHistoryList\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
") = logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->dbHistoryList',\n"
|
|
");\n"
|
|
"\n"
|
|
"my %hDbHash;\n"
|
|
"\n"
|
|
"foreach my $iHistoryId ($self->keys(INFO_ARCHIVE_SECTION_DB_HISTORY))\n"
|
|
"{\n"
|
|
"$hDbHash{$iHistoryId}{&INFO_DB_VERSION} =\n"
|
|
"$self->get(INFO_ARCHIVE_SECTION_DB_HISTORY, $iHistoryId, INFO_ARCHIVE_KEY_DB_VERSION);\n"
|
|
"$hDbHash{$iHistoryId}{&INFO_SYSTEM_ID} =\n"
|
|
"$self->get(INFO_ARCHIVE_SECTION_DB_HISTORY, $iHistoryId, INFO_ARCHIVE_KEY_DB_ID);\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'hDbHash', value => \\%hDbHash}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n\n\n"
|
|
"sub dbSectionSet\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$strDbVersion,\n"
|
|
"$ullDbSysId,\n"
|
|
"$iDbHistoryId,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->dbSectionSet', \\@_,\n"
|
|
"{name => 'strDbVersion', trace => true},\n"
|
|
"{name => 'ullDbSysId', trace => true},\n"
|
|
"{name => 'iDbHistoryId', trace => true}\n"
|
|
");\n"
|
|
"\n\n"
|
|
"$self->numericSet(INFO_ARCHIVE_SECTION_DB, INFO_ARCHIVE_KEY_DB_SYSTEM_ID, undef, $ullDbSysId);\n"
|
|
"$self->set(INFO_ARCHIVE_SECTION_DB, INFO_ARCHIVE_KEY_DB_VERSION, undef, $strDbVersion);\n"
|
|
"$self->numericSet(INFO_ARCHIVE_SECTION_DB, INFO_ARCHIVE_KEY_DB_ID, undef, $iDbHistoryId);\n"
|
|
"\n\n"
|
|
"$self->numericSet(INFO_ARCHIVE_SECTION_DB_HISTORY, $iDbHistoryId, INFO_ARCHIVE_KEY_DB_ID, $ullDbSysId);\n"
|
|
"$self->set(INFO_ARCHIVE_SECTION_DB_HISTORY, $iDbHistoryId, INFO_ARCHIVE_KEY_DB_VERSION, $strDbVersion);\n"
|
|
"\n\n"
|
|
"return logDebugReturn($strOperation);\n"
|
|
"}\n"
|
|
"\n\n\n\n\n\n"
|
|
"sub confirmExists\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"if (!$self->test(INFO_ARCHIVE_SECTION_DB) || !$self->{bExists})\n"
|
|
"{\n"
|
|
"confess &log(ERROR, $strArchiveInfoMissingMsg, ERROR_FILE_MISSING);\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n"
|
|
"1;\n"
|
|
},
|
|
{
|
|
.name = "pgBackRest/Backup/Backup.pm",
|
|
.data =
|
|
"\n\n\n"
|
|
"package pgBackRest::Backup::Backup;\n"
|
|
"\n"
|
|
"use strict;\n"
|
|
"use warnings FATAL => qw(all);\n"
|
|
"use Carp qw(confess);\n"
|
|
"use English '-no_match_vars';\n"
|
|
"\n"
|
|
"use Exporter qw(import);\n"
|
|
"use File::Basename;\n"
|
|
"\n"
|
|
"use pgBackRest::Archive::Common;\n"
|
|
"use pgBackRest::Backup::Common;\n"
|
|
"use pgBackRest::Backup::File;\n"
|
|
"use pgBackRest::Backup::Info;\n"
|
|
"use pgBackRest::Common::Cipher;\n"
|
|
"use pgBackRest::Common::Exception;\n"
|
|
"use pgBackRest::Common::Ini;\n"
|
|
"use pgBackRest::Common::Log;\n"
|
|
"use pgBackRest::Common::Wait;\n"
|
|
"use pgBackRest::Common::String;\n"
|
|
"use pgBackRest::Config::Config;\n"
|
|
"use pgBackRest::Db;\n"
|
|
"use pgBackRest::DbVersion;\n"
|
|
"use pgBackRest::Manifest;\n"
|
|
"use pgBackRest::Protocol::Local::Process;\n"
|
|
"use pgBackRest::Protocol::Helper;\n"
|
|
"use pgBackRest::Protocol::Storage::Helper;\n"
|
|
"use pgBackRest::Common::Io::Handle;\n"
|
|
"use pgBackRest::Storage::Base;\n"
|
|
"use pgBackRest::Storage::Filter::Gzip;\n"
|
|
"use pgBackRest::Storage::Filter::Sha;\n"
|
|
"use pgBackRest::Storage::Helper;\n"
|
|
"use pgBackRest::Version;\n"
|
|
"\n\n\n\n"
|
|
"sub new\n"
|
|
"{\n"
|
|
"my $class = shift;\n"
|
|
"\n\n"
|
|
"my $self = {};\n"
|
|
"bless $self, $class;\n"
|
|
"\n\n"
|
|
"my ($strOperation) = logDebugParam(__PACKAGE__ . '->new');\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'self', value => $self}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub resumeClean\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$oStorageRepo,\n"
|
|
"$strBackupLabel,\n"
|
|
"$oManifest,\n"
|
|
"$oAbortedManifest,\n"
|
|
"$bOnline,\n"
|
|
"$bDelta,\n"
|
|
"$strTimelineCurrent,\n"
|
|
"$strTimelineLast,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->resumeClean', \\@_,\n"
|
|
"{name => 'oStorageRepo'},\n"
|
|
"{name => 'strBackupLabel'},\n"
|
|
"{name => 'oManifest'},\n"
|
|
"{name => 'oAbortedManifest'},\n"
|
|
"{name => 'bOnline'},\n"
|
|
"{name => 'bDelta'},\n"
|
|
"{name => 'strTimelineCurrent', required => false},\n"
|
|
"{name => 'strTimelineLast', required => false},\n"
|
|
");\n"
|
|
"\n"
|
|
"&log(DETAIL, 'clean resumed backup path: ' . $oStorageRepo->pathGet(STORAGE_REPO_BACKUP . \"/${strBackupLabel}\"));\n"
|
|
"\n\n"
|
|
"my $hFile = $oStorageRepo->manifest(STORAGE_REPO_BACKUP . \"/${strBackupLabel}\");\n"
|
|
"\n\n"
|
|
"my $bCompressed = $oAbortedManifest->boolGet(MANIFEST_SECTION_BACKUP_OPTION, MANIFEST_KEY_COMPRESS);\n"
|
|
"\n"
|
|
"if (!$bDelta)\n"
|
|
"{\n"
|
|
"\n"
|
|
"$bDelta = $oAbortedManifest->checkDelta(\n"
|
|
"'resumed', $oAbortedManifest->boolTest(MANIFEST_SECTION_BACKUP_OPTION, MANIFEST_KEY_ONLINE, undef, $bOnline),\n"
|
|
"$strTimelineCurrent, $strTimelineLast);\n"
|
|
"\n\n"
|
|
"if (!$bDelta)\n"
|
|
"{\n"
|
|
"my @stryFileList = ();\n"
|
|
"\n"
|
|
"foreach my $strName (sort(keys(%{$hFile})))\n"
|
|
"{\n"
|
|
"\n"
|
|
"if ($strName eq FILE_MANIFEST_COPY ||\n"
|
|
"$strName eq '.')\n"
|
|
"{\n"
|
|
"next;\n"
|
|
"}\n"
|
|
"\n"
|
|
"if ($hFile->{$strName}{type} eq 'f')\n"
|
|
"{\n"
|
|
"\n"
|
|
"my $strFile = $strName;\n"
|
|
"\n"
|
|
"if ($bCompressed)\n"
|
|
"{\n"
|
|
"$strFile = substr($strFile, 0, length($strFile) - 3);\n"
|
|
"}\n"
|
|
"\n\n\n"
|
|
"if ($oManifest->test(MANIFEST_SECTION_TARGET_FILE, $strFile) &&\n"
|
|
"!$oManifest->test(MANIFEST_SECTION_TARGET_FILE, $strFile, MANIFEST_SUBKEY_REFERENCE) &&\n"
|
|
"$oAbortedManifest->test(MANIFEST_SECTION_TARGET_FILE, $strFile, MANIFEST_SUBKEY_CHECKSUM))\n"
|
|
"{\n"
|
|
"push(@stryFileList, $strFile);\n"
|
|
"}\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if (@stryFileList)\n"
|
|
"{\n"
|
|
"$bDelta = $oManifest->checkDeltaFile(\\@stryFileList, $oAbortedManifest, undef);\n"
|
|
"}\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"my @stryFile;\n"
|
|
"\n"
|
|
"foreach my $strName (sort(keys(%{$hFile})))\n"
|
|
"{\n"
|
|
"\n"
|
|
"if ($strName eq FILE_MANIFEST_COPY ||\n"
|
|
"$strName eq '.')\n"
|
|
"{\n"
|
|
"next;\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"my $cType = $hFile->{$strName}{type};\n"
|
|
"\n\n"
|
|
"if ($cType eq 'd')\n"
|
|
"{\n"
|
|
"if ($oManifest->test(MANIFEST_SECTION_TARGET_PATH, $strName))\n"
|
|
"{\n"
|
|
"next;\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n"
|
|
"elsif ($cType eq 'f')\n"
|
|
"{\n"
|
|
"\n"
|
|
"my $strFile = $strName;\n"
|
|
"\n"
|
|
"if ($bCompressed)\n"
|
|
"{\n"
|
|
"$strFile = substr($strFile, 0, length($strFile) - 3);\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if ($oManifest->test(MANIFEST_SECTION_TARGET_FILE, $strFile) &&\n"
|
|
"!$oManifest->test(MANIFEST_SECTION_TARGET_FILE, $strFile, MANIFEST_SUBKEY_REFERENCE))\n"
|
|
"{\n"
|
|
"\n"
|
|
"my $strChecksum = $oAbortedManifest->get(MANIFEST_SECTION_TARGET_FILE, $strFile, MANIFEST_SUBKEY_CHECKSUM, false);\n"
|
|
"\n\n\n\n\n"
|
|
"if (defined($strChecksum) &&\n"
|
|
"$oManifest->get(MANIFEST_SECTION_TARGET_FILE, $strFile, MANIFEST_SUBKEY_SIZE) ==\n"
|
|
"$oAbortedManifest->get(MANIFEST_SECTION_TARGET_FILE, $strFile, MANIFEST_SUBKEY_SIZE) &&\n"
|
|
"($bDelta ||\n"
|
|
"$oManifest->get(MANIFEST_SECTION_TARGET_FILE, $strFile, MANIFEST_SUBKEY_TIMESTAMP) ==\n"
|
|
"$oAbortedManifest->get(MANIFEST_SECTION_TARGET_FILE, $strFile, MANIFEST_SUBKEY_TIMESTAMP)))\n"
|
|
"{\n"
|
|
"$oManifest->set(MANIFEST_SECTION_TARGET_FILE, $strFile, MANIFEST_SUBKEY_CHECKSUM, $strChecksum);\n"
|
|
"\n\n"
|
|
"my $bChecksumPage =\n"
|
|
"$oAbortedManifest->get(MANIFEST_SECTION_TARGET_FILE, $strFile, MANIFEST_SUBKEY_CHECKSUM_PAGE, false);\n"
|
|
"\n"
|
|
"if (defined($bChecksumPage))\n"
|
|
"{\n"
|
|
"$oManifest->boolSet(MANIFEST_SECTION_TARGET_FILE, $strFile, MANIFEST_SUBKEY_CHECKSUM_PAGE, $bChecksumPage);\n"
|
|
"\n"
|
|
"if (!$bChecksumPage &&\n"
|
|
"$oAbortedManifest->test(MANIFEST_SECTION_TARGET_FILE, $strFile, MANIFEST_SUBKEY_CHECKSUM_PAGE_ERROR))\n"
|
|
"{\n"
|
|
"$oManifest->set(\n"
|
|
"MANIFEST_SECTION_TARGET_FILE, $strFile, MANIFEST_SUBKEY_CHECKSUM_PAGE_ERROR,\n"
|
|
"$oAbortedManifest->get(\n"
|
|
"MANIFEST_SECTION_TARGET_FILE, $strFile, MANIFEST_SUBKEY_CHECKSUM_PAGE_ERROR));\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n"
|
|
"next;\n"
|
|
"}\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if ($cType eq 'd')\n"
|
|
"{\n"
|
|
"logDebugMisc($strOperation, \"remove path ${strName}\");\n"
|
|
"$oStorageRepo->remove(STORAGE_REPO_BACKUP . \"/${strBackupLabel}/${strName}\", {bRecurse => true});\n"
|
|
"}\n"
|
|
"\n"
|
|
"else\n"
|
|
"{\n"
|
|
"logDebugMisc($strOperation, \"remove file ${strName}\");\n"
|
|
"push(@stryFile, STORAGE_REPO_BACKUP . \"/${strBackupLabel}/${strName}\");\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if (@stryFile > 0)\n"
|
|
"{\n"
|
|
"$oStorageRepo->remove(\\@stryFile);\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'bDelta', value => $bDelta, trace => true},\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n\n\n\n"
|
|
"sub processManifest\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$strDbMasterPath,\n"
|
|
"$strDbCopyPath,\n"
|
|
"$strType,\n"
|
|
"$strDbVersion,\n"
|
|
"$bCompress,\n"
|
|
"$bHardLink,\n"
|
|
"$oBackupManifest,\n"
|
|
"$strBackupLabel,\n"
|
|
"$strLsnStart,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->processManifest', \\@_,\n"
|
|
"{name => 'strDbMasterPath'},\n"
|
|
"{name => 'strDbCopyPath'},\n"
|
|
"{name => 'strType'},\n"
|
|
"{name => 'strDbVersion'},\n"
|
|
"{name => 'bCompress'},\n"
|
|
"{name => 'bHardLink'},\n"
|
|
"{name => 'oBackupManifest'},\n"
|
|
"{name => 'strBackupLabel'},\n"
|
|
"{name => 'strLsnStart', required => false},\n"
|
|
");\n"
|
|
"\n\n"
|
|
"&log(TEST, TEST_BACKUP_START);\n"
|
|
"\n\n"
|
|
"my $oProtocolMaster =\n"
|
|
"!isDbLocal({iRemoteIdx => $self->{iMasterRemoteIdx}}) ?\n"
|
|
"protocolGet(CFGOPTVAL_REMOTE_TYPE_DB, $self->{iMasterRemoteIdx}) : undef;\n"
|
|
"defined($oProtocolMaster) && $oProtocolMaster->noOp();\n"
|
|
"\n\n"
|
|
"my $oBackupProcess = new pgBackRest::Protocol::Local::Process(CFGOPTVAL_LOCAL_TYPE_DB);\n"
|
|
"\n"
|
|
"if ($self->{iCopyRemoteIdx} != $self->{iMasterRemoteIdx})\n"
|
|
"{\n"
|
|
"$oBackupProcess->hostAdd($self->{iMasterRemoteIdx}, 1);\n"
|
|
"}\n"
|
|
"\n"
|
|
"$oBackupProcess->hostAdd($self->{iCopyRemoteIdx}, cfgOption(CFGOPT_PROCESS_MAX));\n"
|
|
"\n\n"
|
|
"my $lFileTotal = 0;\n"
|
|
"my $lSizeTotal = 0;\n"
|
|
"\n\n"
|
|
"if ($bHardLink || $strType eq CFGOPTVAL_BACKUP_TYPE_FULL)\n"
|
|
"{\n"
|
|
"\n"
|
|
"foreach my $strPath ($oBackupManifest->keys(MANIFEST_SECTION_TARGET_PATH))\n"
|
|
"{\n"
|
|
"storageRepo()->pathCreate(STORAGE_REPO_BACKUP . \"/${strBackupLabel}/${strPath}\", {bIgnoreExists => true});\n"
|
|
"}\n"
|
|
"\n"
|
|
"if (storageRepo()->driver()->capability(STORAGE_CAPABILITY_LINK))\n"
|
|
"{\n"
|
|
"for my $strTarget ($oBackupManifest->keys(MANIFEST_SECTION_BACKUP_TARGET))\n"
|
|
"{\n"
|
|
"if ($oBackupManifest->isTargetTablespace($strTarget))\n"
|
|
"{\n"
|
|
"storageRepo()->linkCreate(\n"
|
|
"STORAGE_REPO_BACKUP . \"/${strBackupLabel}/${strTarget}\",\n"
|
|
"STORAGE_REPO_BACKUP . \"/${strBackupLabel}/\" . MANIFEST_TARGET_PGDATA . \"/${strTarget}\",\n"
|
|
"{bRelative => true});\n"
|
|
"}\n"
|
|
"}\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"my $hStartLsnParam =\n"
|
|
"{\n"
|
|
"iWalId => defined($strLsnStart) ? hex((split('/', $strLsnStart))[0]) : 0xFFFF,\n"
|
|
"iWalOffset => defined($strLsnStart) ? hex((split('/', $strLsnStart))[1]) : 0xFFFF,\n"
|
|
"};\n"
|
|
"\n\n"
|
|
"foreach my $strRepoFile (\n"
|
|
"sort {sprintf(\"%016d-%s\", $oBackupManifest->numericGet(MANIFEST_SECTION_TARGET_FILE, $b, MANIFEST_SUBKEY_SIZE), $b) cmp\n"
|
|
"sprintf(\"%016d-%s\", $oBackupManifest->numericGet(MANIFEST_SECTION_TARGET_FILE, $a, MANIFEST_SUBKEY_SIZE), $a)}\n"
|
|
"($oBackupManifest->keys(MANIFEST_SECTION_TARGET_FILE, INI_SORT_NONE)))\n"
|
|
"{\n"
|
|
"\n\n"
|
|
"my $strReference = $oBackupManifest->get(MANIFEST_SECTION_TARGET_FILE, $strRepoFile, MANIFEST_SUBKEY_REFERENCE, false);\n"
|
|
"\n"
|
|
"if (defined($strReference))\n"
|
|
"{\n"
|
|
"\n\n"
|
|
"if (!cfgOption(CFGOPT_DELTA) ||\n"
|
|
"$oBackupManifest->numericGet(MANIFEST_SECTION_TARGET_FILE, $strRepoFile, MANIFEST_SUBKEY_SIZE) == 0)\n"
|
|
"{\n"
|
|
"\n"
|
|
"next;\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"my $strQueueKey = MANIFEST_TARGET_PGDATA;\n"
|
|
"\n\n"
|
|
"if (index($strRepoFile, DB_PATH_PGTBLSPC . '/') == 0)\n"
|
|
"{\n"
|
|
"$strQueueKey = DB_PATH_PGTBLSPC . '/' . (split('\\/', $strRepoFile))[1];\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"my $bIgnoreMissing = true;\n"
|
|
"my $strDbFile = $oBackupManifest->dbPathGet($strDbCopyPath, $strRepoFile);\n"
|
|
"my $iHostConfigIdx = $self->{iCopyRemoteIdx};\n"
|
|
"\n\n"
|
|
"if ($oBackupManifest->boolGet(MANIFEST_SECTION_TARGET_FILE, $strRepoFile, MANIFEST_SUBKEY_MASTER))\n"
|
|
"{\n"
|
|
"$strDbFile = $oBackupManifest->dbPathGet($strDbMasterPath, $strRepoFile);\n"
|
|
"$iHostConfigIdx = $self->{iMasterRemoteIdx};\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if ($strRepoFile eq MANIFEST_TARGET_PGDATA . '/' . DB_FILE_PGCONTROL)\n"
|
|
"{\n"
|
|
"$bIgnoreMissing = false;\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"my $lSize = $oBackupManifest->numericGet(MANIFEST_SECTION_TARGET_FILE, $strRepoFile, MANIFEST_SUBKEY_SIZE);\n"
|
|
"\n"
|
|
"$lFileTotal++;\n"
|
|
"$lSizeTotal += $lSize;\n"
|
|
"\n\n"
|
|
"$oBackupProcess->queueJob(\n"
|
|
"$iHostConfigIdx, $strQueueKey, $strRepoFile, OP_BACKUP_FILE,\n"
|
|
"[$strDbFile, $strRepoFile, $lSize,\n"
|
|
"$oBackupManifest->get(MANIFEST_SECTION_TARGET_FILE, $strRepoFile, MANIFEST_SUBKEY_CHECKSUM, false),\n"
|
|
"cfgOption(CFGOPT_CHECKSUM_PAGE) ? isChecksumPage($strRepoFile) : false, $strBackupLabel, $bCompress,\n"
|
|
"cfgOption(CFGOPT_COMPRESS_LEVEL), $oBackupManifest->numericGet(MANIFEST_SECTION_TARGET_FILE, $strRepoFile,\n"
|
|
"MANIFEST_SUBKEY_TIMESTAMP, false), $bIgnoreMissing,\n"
|
|
"cfgOption(CFGOPT_CHECKSUM_PAGE) && isChecksumPage($strRepoFile) ? $hStartLsnParam : undef,\n"
|
|
"cfgOption(CFGOPT_DELTA), defined($strReference) ? true : false],\n"
|
|
"{rParamSecure => $oBackupManifest->cipherPassSub() ? [$oBackupManifest->cipherPassSub()] : undef});\n"
|
|
"\n\n"
|
|
"$oBackupManifest->remove(MANIFEST_SECTION_TARGET_FILE, $strRepoFile, MANIFEST_SUBKEY_SIZE);\n"
|
|
"$oBackupManifest->remove(MANIFEST_SECTION_TARGET_FILE, $strRepoFile, MANIFEST_SUBKEY_CHECKSUM);\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if (!$oBackupManifest->test(MANIFEST_SECTION_TARGET_FILE, MANIFEST_FILE_PGCONTROL) && cfgOption(CFGOPT_ONLINE))\n"
|
|
"{\n"
|
|
"confess &log(ERROR, DB_FILE_PGCONTROL . \" must be present in all online backups\\n\" .\n"
|
|
"'HINT: is something wrong with the clock or filesystem timestamps?', ERROR_FILE_MISSING);\n"
|
|
"}\n"
|
|
"\n\n\n"
|
|
"if ($lFileTotal == 0 && !cfgOption(CFGOPT_TEST))\n"
|
|
"{\n"
|
|
"confess &log(ERROR, \"no files have changed since the last backup - this seems unlikely\", ERROR_FILE_MISSING);\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"my $lSizeCurrent = 0;\n"
|
|
"\n\n"
|
|
"my $lManifestSaveCurrent = 0;\n"
|
|
"my $lManifestSaveSize = int($lSizeTotal / 100);\n"
|
|
"\n"
|
|
"if (cfgOptionSource(CFGOPT_MANIFEST_SAVE_THRESHOLD) ne CFGDEF_SOURCE_DEFAULT ||\n"
|
|
"$lManifestSaveSize < cfgOption(CFGOPT_MANIFEST_SAVE_THRESHOLD))\n"
|
|
"{\n"
|
|
"$lManifestSaveSize = cfgOption(CFGOPT_MANIFEST_SAVE_THRESHOLD);\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"while (my $hyJob = $oBackupProcess->process())\n"
|
|
"{\n"
|
|
"foreach my $hJob (@{$hyJob})\n"
|
|
"{\n"
|
|
"($lSizeCurrent, $lManifestSaveCurrent) = backupManifestUpdate(\n"
|
|
"$oBackupManifest, cfgOption(cfgOptionIdFromIndex(CFGOPT_PG_HOST, $hJob->{iHostConfigIdx}), false),\n"
|
|
"$hJob->{iProcessId}, @{$hJob->{rParam}}[0..4], @{$hJob->{rResult}}, $lSizeTotal, $lSizeCurrent, $lManifestSaveSize,\n"
|
|
"$lManifestSaveCurrent);\n"
|
|
"}\n"
|
|
"\n\n\n"
|
|
"protocolKeepAlive();\n"
|
|
"}\n"
|
|
"\n"
|
|
"foreach my $strFile ($oBackupManifest->keys(MANIFEST_SECTION_TARGET_FILE))\n"
|
|
"{\n"
|
|
"\n\n"
|
|
"my $strReference = $oBackupManifest->get(MANIFEST_SECTION_TARGET_FILE, $strFile, MANIFEST_SUBKEY_REFERENCE, false);\n"
|
|
"\n"
|
|
"if ($strReference)\n"
|
|
"{\n"
|
|
"\n"
|
|
"if ($bHardLink)\n"
|
|
"{\n"
|
|
"&log(DETAIL, \"hardlink ${strFile} to ${strReference}\");\n"
|
|
"\n"
|
|
"storageRepo()->linkCreate(\n"
|
|
"STORAGE_REPO_BACKUP . \"/${strReference}/${strFile}\" . ($bCompress ? qw{.} . COMPRESS_EXT : ''),\n"
|
|
"STORAGE_REPO_BACKUP . \"/${strBackupLabel}/${strFile}\" . ($bCompress ? qw{.} . COMPRESS_EXT : ''),\n"
|
|
"{bHard => true});\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"else\n"
|
|
"{\n"
|
|
"logDebugMisc($strOperation, \"reference ${strFile} to ${strReference}\");\n"
|
|
"}\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"$oBackupManifest->validate();\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'lSizeTotal', value => $lSizeTotal}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n\n\n"
|
|
"sub process\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my ($strOperation) = logDebugParam(__PACKAGE__ . '->process');\n"
|
|
"\n\n"
|
|
"my $lTimestampStart = time();\n"
|
|
"\n\n"
|
|
"my $oStorageRepo = storageRepo();\n"
|
|
"\n\n"
|
|
"my $strType = cfgOption(CFGOPT_TYPE);\n"
|
|
"my $bCompress = cfgOption(CFGOPT_COMPRESS);\n"
|
|
"my $bHardLink = cfgOption(CFGOPT_REPO_HARDLINK);\n"
|
|
"\n\n"
|
|
"my $oBackupInfo = new pgBackRest::Backup::Info($oStorageRepo->pathGet(STORAGE_REPO_BACKUP));\n"
|
|
"\n\n"
|
|
"my $strCipherPassManifest = $oBackupInfo->cipherPassSub();\n"
|
|
"my $strCipherPassBackupSet;\n"
|
|
"\n\n"
|
|
"my $oDbMaster = undef;\n"
|
|
"my $oDbStandby = undef;\n"
|
|
"\n\n"
|
|
"($oDbMaster, $self->{iMasterRemoteIdx}, $oDbStandby, $self->{iCopyRemoteIdx}) = dbObjectGet();\n"
|
|
"\n\n"
|
|
"if (!defined($self->{iCopyRemoteIdx}))\n"
|
|
"{\n"
|
|
"$self->{iCopyRemoteIdx} = $self->{iMasterRemoteIdx};\n"
|
|
"}\n"
|
|
"\n\n\n"
|
|
"if (!defined($oDbStandby) && cfgOption(CFGOPT_BACKUP_STANDBY))\n"
|
|
"{\n"
|
|
"cfgOptionSet(CFGOPT_BACKUP_STANDBY, false);\n"
|
|
"&log(WARN, 'option backup-standby is enabled but standby is not properly configured - ' .\n"
|
|
"'backups will be performed from the master');\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"my $oStorageDbMaster = storageDb({iRemoteIdx => $self->{iMasterRemoteIdx}});\n"
|
|
"\n\n"
|
|
"my $strDbMasterPath = cfgOption(cfgOptionIdFromIndex(CFGOPT_PG_PATH, $self->{iMasterRemoteIdx}));\n"
|
|
"my $strDbCopyPath = cfgOption(cfgOptionIdFromIndex(CFGOPT_PG_PATH, $self->{iCopyRemoteIdx}));\n"
|
|
"\n\n"
|
|
"my ($strDbVersion, $iControlVersion, $iCatalogVersion, $ullDbSysId) = $oDbMaster->info();\n"
|
|
"\n"
|
|
"my $iDbHistoryId = $oBackupInfo->check($strDbVersion, $iControlVersion, $iCatalogVersion, $ullDbSysId);\n"
|
|
"\n\n"
|
|
"my $oLastManifest;\n"
|
|
"my $strBackupLastPath;\n"
|
|
"my $strTimelineLast;\n"
|
|
"\n"
|
|
"if ($strType ne CFGOPTVAL_BACKUP_TYPE_FULL)\n"
|
|
"{\n"
|
|
"$strBackupLastPath = $oBackupInfo->last(\n"
|
|
"$strType eq CFGOPTVAL_BACKUP_TYPE_DIFF ? CFGOPTVAL_BACKUP_TYPE_FULL : CFGOPTVAL_BACKUP_TYPE_INCR);\n"
|
|
"\n\n"
|
|
"if (defined($strBackupLastPath) && $oBackupInfo->confirmDb($strBackupLastPath, $strDbVersion, $ullDbSysId))\n"
|
|
"{\n"
|
|
"$oLastManifest = new pgBackRest::Manifest(\n"
|
|
"$oStorageRepo->pathGet(STORAGE_REPO_BACKUP . \"/${strBackupLastPath}/\" . FILE_MANIFEST),\n"
|
|
"{strCipherPass => $strCipherPassManifest});\n"
|
|
"\n\n"
|
|
"$strCipherPassBackupSet = $oLastManifest->cipherPassSub();\n"
|
|
"\n\n"
|
|
"if ($oLastManifest->test(MANIFEST_SECTION_BACKUP, MANIFEST_KEY_ARCHIVE_STOP))\n"
|
|
"{\n"
|
|
"$strTimelineLast = substr($oLastManifest->get(MANIFEST_SECTION_BACKUP, MANIFEST_KEY_ARCHIVE_STOP), 0, 8);\n"
|
|
"}\n"
|
|
"\n"
|
|
"&log(INFO, 'last backup label = ' . $oLastManifest->get(MANIFEST_SECTION_BACKUP, MANIFEST_KEY_LABEL) .\n"
|
|
"', version = ' . $oLastManifest->get(INI_SECTION_BACKREST, INI_KEY_VERSION));\n"
|
|
"\n\n"
|
|
"my $strKey;\n"
|
|
"\n\n"
|
|
"if (!$oLastManifest->boolTest(MANIFEST_SECTION_BACKUP_OPTION, MANIFEST_KEY_COMPRESS, undef, $bCompress))\n"
|
|
"{\n"
|
|
"&log(WARN, \"${strType} backup cannot alter compress option to '\" . boolFormat($bCompress) .\n"
|
|
"\"', reset to value in ${strBackupLastPath}\");\n"
|
|
"$bCompress = $oLastManifest->boolGet(MANIFEST_SECTION_BACKUP_OPTION, MANIFEST_KEY_COMPRESS);\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if (!$oLastManifest->boolTest(MANIFEST_SECTION_BACKUP_OPTION, MANIFEST_KEY_HARDLINK, undef, $bHardLink))\n"
|
|
"{\n"
|
|
"&log(WARN, \"${strType} backup cannot alter hardlink option to '\" . boolFormat($bHardLink) .\n"
|
|
"\"', reset to value in ${strBackupLastPath}\");\n"
|
|
"$bHardLink = $oLastManifest->boolGet(MANIFEST_SECTION_BACKUP_OPTION, MANIFEST_KEY_HARDLINK);\n"
|
|
"}\n"
|
|
"}\n"
|
|
"else\n"
|
|
"{\n"
|
|
"&log(WARN, \"no prior backup exists, ${strType} backup has been changed to full\");\n"
|
|
"$strType = CFGOPTVAL_BACKUP_TYPE_FULL;\n"
|
|
"$strBackupLastPath = undef;\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"my $strBackupLabel;\n"
|
|
"my $oAbortedManifest;\n"
|
|
"my $strBackupPath;\n"
|
|
"my $strTimelineAborted;\n"
|
|
"\n"
|
|
"foreach my $strAbortedBackup ($oStorageRepo->list(\n"
|
|
"STORAGE_REPO_BACKUP, {strExpression => backupRegExpGet(true, true, true), strSortOrder => 'reverse'}))\n"
|
|
"{\n"
|
|
"\n"
|
|
"if ($oStorageRepo->exists(STORAGE_REPO_BACKUP . \"/${strAbortedBackup}/\" . FILE_MANIFEST_COPY) &&\n"
|
|
"!$oStorageRepo->exists(STORAGE_REPO_BACKUP . \"/${strAbortedBackup}/\" . FILE_MANIFEST))\n"
|
|
"{\n"
|
|
"my $bUsable;\n"
|
|
"my $strReason = \"resume is disabled\";\n"
|
|
"$strBackupPath = $oStorageRepo->pathGet(STORAGE_REPO_BACKUP . \"/${strAbortedBackup}\");\n"
|
|
"\n\n\n"
|
|
"if (cfgOption(CFGOPT_RESUME))\n"
|
|
"{\n"
|
|
"$strReason = \"unable to read ${strBackupPath}/\" . FILE_MANIFEST;\n"
|
|
"\n"
|
|
"eval\n"
|
|
"{\n"
|
|
"\n"
|
|
"$oAbortedManifest = new pgBackRest::Manifest(\"${strBackupPath}/\" . FILE_MANIFEST,\n"
|
|
"{strCipherPass => $strCipherPassManifest});\n"
|
|
"\n\n"
|
|
"my $strKey;\n"
|
|
"my $strValueNew;\n"
|
|
"my $strValueAborted;\n"
|
|
"\n\n"
|
|
"if ($oAbortedManifest->get(INI_SECTION_BACKREST, INI_KEY_VERSION) ne PROJECT_VERSION)\n"
|
|
"{\n"
|
|
"$strKey = INI_KEY_VERSION;\n"
|
|
"$strValueNew = PROJECT_VERSION;\n"
|
|
"$strValueAborted = $oAbortedManifest->get(INI_SECTION_BACKREST, INI_KEY_VERSION);\n"
|
|
"}\n"
|
|
"\n"
|
|
"elsif ($oAbortedManifest->get(INI_SECTION_BACKREST, INI_KEY_FORMAT) ne REPOSITORY_FORMAT)\n"
|
|
"{\n"
|
|
"$strKey = INI_KEY_FORMAT;\n"
|
|
"$strValueNew = REPOSITORY_FORMAT;\n"
|
|
"$strValueAborted = $oAbortedManifest->get(INI_SECTION_BACKREST, INI_KEY_FORMAT);\n"
|
|
"}\n"
|
|
"\n"
|
|
"elsif ($oAbortedManifest->get(MANIFEST_SECTION_BACKUP, MANIFEST_KEY_TYPE) ne $strType)\n"
|
|
"{\n"
|
|
"$strKey = MANIFEST_KEY_TYPE;\n"
|
|
"$strValueNew = $strType;\n"
|
|
"$strValueAborted = $oAbortedManifest->get(MANIFEST_SECTION_BACKUP, MANIFEST_KEY_TYPE);\n"
|
|
"}\n"
|
|
"\n"
|
|
"elsif ($oAbortedManifest->get(MANIFEST_SECTION_BACKUP, MANIFEST_KEY_PRIOR, undef, false, '<undef>') ne\n"
|
|
"(defined($strBackupLastPath) ? $strBackupLastPath : '<undef>'))\n"
|
|
"{\n"
|
|
"$strKey = MANIFEST_KEY_PRIOR;\n"
|
|
"$strValueNew = defined($strBackupLastPath) ? $strBackupLastPath : '<undef>';\n"
|
|
"$strValueAborted =\n"
|
|
"$oAbortedManifest->get(MANIFEST_SECTION_BACKUP, MANIFEST_KEY_PRIOR, undef, false, '<undef>');\n"
|
|
"}\n"
|
|
"\n"
|
|
"elsif ($oAbortedManifest->boolGet(MANIFEST_SECTION_BACKUP_OPTION, MANIFEST_KEY_COMPRESS) !=\n"
|
|
"cfgOption(CFGOPT_COMPRESS))\n"
|
|
"{\n"
|
|
"$strKey = MANIFEST_KEY_COMPRESS;\n"
|
|
"$strValueNew = cfgOption(CFGOPT_COMPRESS);\n"
|
|
"$strValueAborted = $oAbortedManifest->boolGet(MANIFEST_SECTION_BACKUP_OPTION, MANIFEST_KEY_COMPRESS);\n"
|
|
"}\n"
|
|
"\n"
|
|
"elsif ($oAbortedManifest->boolGet(MANIFEST_SECTION_BACKUP_OPTION, MANIFEST_KEY_HARDLINK) !=\n"
|
|
"cfgOption(CFGOPT_REPO_HARDLINK))\n"
|
|
"{\n"
|
|
"$strKey = MANIFEST_KEY_HARDLINK;\n"
|
|
"$strValueNew = cfgOption(CFGOPT_REPO_HARDLINK);\n"
|
|
"$strValueAborted = $oAbortedManifest->boolGet(MANIFEST_SECTION_BACKUP_OPTION, MANIFEST_KEY_HARDLINK);\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if (defined($strKey))\n"
|
|
"{\n"
|
|
"$strReason = \"new ${strKey} '${strValueNew}' does not match aborted ${strKey} '${strValueAborted}'\";\n"
|
|
"}\n"
|
|
"\n"
|
|
"else\n"
|
|
"{\n"
|
|
"$bUsable = true;\n"
|
|
"}\n"
|
|
"\n"
|
|
"return true;\n"
|
|
"}\n"
|
|
"or do\n"
|
|
"{\n"
|
|
"$bUsable = false;\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if ($bUsable)\n"
|
|
"{\n"
|
|
"$strBackupLabel = $strAbortedBackup;\n"
|
|
"\n\n"
|
|
"if (defined($strCipherPassManifest))\n"
|
|
"{\n"
|
|
"$strCipherPassBackupSet = $oAbortedManifest->cipherPassSub();\n"
|
|
"}\n"
|
|
"\n\n\n"
|
|
"if ($oAbortedManifest->test(MANIFEST_SECTION_BACKUP, MANIFEST_KEY_ARCHIVE_STOP))\n"
|
|
"{\n"
|
|
"$strTimelineAborted = substr($oAbortedManifest->get(MANIFEST_SECTION_BACKUP, MANIFEST_KEY_ARCHIVE_STOP), 0, 8);\n"
|
|
"}\n"
|
|
"elsif ($oAbortedManifest->test(MANIFEST_SECTION_BACKUP, MANIFEST_KEY_ARCHIVE_START))\n"
|
|
"{\n"
|
|
"$strTimelineAborted = substr($oAbortedManifest->get(MANIFEST_SECTION_BACKUP, MANIFEST_KEY_ARCHIVE_START), 0, 8);\n"
|
|
"}\n"
|
|
"}\n"
|
|
"else\n"
|
|
"{\n"
|
|
"&log(WARN, \"aborted backup ${strAbortedBackup} cannot be resumed: ${strReason}\");\n"
|
|
"&log(TEST, TEST_BACKUP_NORESUME);\n"
|
|
"\n"
|
|
"$oStorageRepo->remove(STORAGE_REPO_BACKUP . \"/${strAbortedBackup}\", {bRecurse => true});\n"
|
|
"undef($oAbortedManifest);\n"
|
|
"}\n"
|
|
"\n"
|
|
"last;\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if (defined($strCipherPassManifest) && !defined($strCipherPassBackupSet) && $strType eq CFGOPTVAL_BACKUP_TYPE_FULL)\n"
|
|
"{\n"
|
|
"$strCipherPassBackupSet = cipherPassGen();\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if (!defined($strBackupLabel))\n"
|
|
"{\n"
|
|
"$strBackupLabel = backupLabel($oStorageRepo, $strType, $strBackupLastPath, $lTimestampStart);\n"
|
|
"$strBackupPath = $oStorageRepo->pathGet(STORAGE_REPO_BACKUP . \"/${strBackupLabel}\");\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"my $oBackupManifest = new pgBackRest::Manifest(\"$strBackupPath/\" . FILE_MANIFEST,\n"
|
|
"{bLoad => false, strDbVersion => $strDbVersion, iDbCatalogVersion => $iCatalogVersion,\n"
|
|
"strCipherPass => defined($strCipherPassManifest) ? $strCipherPassManifest : undef,\n"
|
|
"strCipherPassSub => defined($strCipherPassManifest) ? $strCipherPassBackupSet : undef});\n"
|
|
"\n\n"
|
|
"$oBackupManifest->set(MANIFEST_SECTION_BACKUP, MANIFEST_KEY_TYPE, undef, $strType);\n"
|
|
"$oBackupManifest->numericSet(MANIFEST_SECTION_BACKUP, MANIFEST_KEY_TIMESTAMP_START, undef, $lTimestampStart);\n"
|
|
"$oBackupManifest->boolSet(MANIFEST_SECTION_BACKUP_OPTION, MANIFEST_KEY_BACKUP_STANDBY, undef, cfgOption(CFGOPT_BACKUP_STANDBY));\n"
|
|
"$oBackupManifest->numericSet(MANIFEST_SECTION_BACKUP_OPTION, MANIFEST_KEY_BUFFER_SIZE, undef, cfgOption(CFGOPT_BUFFER_SIZE));\n"
|
|
"$oBackupManifest->boolSet(MANIFEST_SECTION_BACKUP_OPTION, MANIFEST_KEY_COMPRESS, undef, $bCompress);\n"
|
|
"$oBackupManifest->numericSet(\n"
|
|
"MANIFEST_SECTION_BACKUP_OPTION, MANIFEST_KEY_COMPRESS_LEVEL, undef, cfgOption(CFGOPT_COMPRESS_LEVEL));\n"
|
|
"$oBackupManifest->numericSet(\n"
|
|
"MANIFEST_SECTION_BACKUP_OPTION, MANIFEST_KEY_COMPRESS_LEVEL_NETWORK, undef, cfgOption(CFGOPT_COMPRESS_LEVEL_NETWORK));\n"
|
|
"$oBackupManifest->boolSet(MANIFEST_SECTION_BACKUP_OPTION, MANIFEST_KEY_HARDLINK, undef, $bHardLink);\n"
|
|
"$oBackupManifest->boolSet(MANIFEST_SECTION_BACKUP_OPTION, MANIFEST_KEY_ONLINE, undef, cfgOption(CFGOPT_ONLINE));\n"
|
|
"$oBackupManifest->boolSet(MANIFEST_SECTION_BACKUP_OPTION, MANIFEST_KEY_ARCHIVE_COPY, undef,\n"
|
|
"!cfgOption(CFGOPT_ONLINE) ||\n"
|
|
"(cfgOption(CFGOPT_ARCHIVE_CHECK) && cfgOption(CFGOPT_ARCHIVE_COPY)));\n"
|
|
"$oBackupManifest->boolSet(MANIFEST_SECTION_BACKUP_OPTION, MANIFEST_KEY_ARCHIVE_CHECK, undef,\n"
|
|
"!cfgOption(CFGOPT_ONLINE) || cfgOption(CFGOPT_ARCHIVE_CHECK));\n"
|
|
"$oBackupManifest->numericSet(MANIFEST_SECTION_BACKUP_OPTION, MANIFEST_KEY_PROCESS_MAX, undef, cfgOption(CFGOPT_PROCESS_MAX));\n"
|
|
"\n\n"
|
|
"$oBackupManifest->numericSet(MANIFEST_SECTION_BACKUP_DB, MANIFEST_KEY_DB_ID, undef, $iDbHistoryId);\n"
|
|
"$oBackupManifest->numericSet(MANIFEST_SECTION_BACKUP_DB, MANIFEST_KEY_CONTROL, undef, $iControlVersion);\n"
|
|
"$oBackupManifest->numericSet(MANIFEST_SECTION_BACKUP_DB, MANIFEST_KEY_SYSTEM_ID, undef, $ullDbSysId);\n"
|
|
"\n\n"
|
|
"if (cfgOption(CFGOPT_ONLINE) && cfgOption(CFGOPT_BACKUP_STANDBY) && $strDbVersion < PG_VERSION_BACKUP_STANDBY)\n"
|
|
"{\n"
|
|
"confess &log(ERROR,\n"
|
|
"'option \\'' . cfgOptionName(CFGOPT_BACKUP_STANDBY) . '\\' not valid for PostgreSQL < ' . PG_VERSION_BACKUP_STANDBY,\n"
|
|
"ERROR_CONFIG);\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"my $strArchiveStart = undef;\n"
|
|
"my $strLsnStart = undef;\n"
|
|
"my $iWalSegmentSize = undef;\n"
|
|
"my $hTablespaceMap = undef;\n"
|
|
"my $hDatabaseMap = undef;\n"
|
|
"my $strTimelineCurrent = undef;\n"
|
|
"\n\n"
|
|
"if (!cfgOption(CFGOPT_ONLINE))\n"
|
|
"{\n"
|
|
"\n\n\n"
|
|
"if (!cfgOptionTest(CFGOPT_CHECKSUM_PAGE))\n"
|
|
"{\n"
|
|
"cfgOptionSet(CFGOPT_CHECKSUM_PAGE, false);\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if ($oStorageDbMaster->exists($strDbMasterPath . '/' . DB_FILE_POSTMASTERPID))\n"
|
|
"{\n"
|
|
"if (cfgOption(CFGOPT_FORCE))\n"
|
|
"{\n"
|
|
"&log(WARN, '--no-online passed and ' . DB_FILE_POSTMASTERPID . ' exists but --force was passed so backup will ' .\n"
|
|
"'continue though it looks like the postmaster is running and the backup will probably not be ' .\n"
|
|
"'consistent');\n"
|
|
"}\n"
|
|
"else\n"
|
|
"{\n"
|
|
"confess &log(ERROR, '--no-online passed but ' . DB_FILE_POSTMASTERPID . ' exists - looks like the postmaster is ' .\n"
|
|
"'running. Shutdown the postmaster and try again, or use --force.', ERROR_POSTMASTER_RUNNING);\n"
|
|
"}\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n"
|
|
"else\n"
|
|
"{\n"
|
|
"\n"
|
|
"($strArchiveStart, $strLsnStart, $iWalSegmentSize) =\n"
|
|
"$oDbMaster->backupStart(\n"
|
|
"PROJECT_NAME . ' backup started at ' . timestampFormat(undef, $lTimestampStart), cfgOption(CFGOPT_START_FAST));\n"
|
|
"\n\n"
|
|
"$oBackupManifest->set(MANIFEST_SECTION_BACKUP, MANIFEST_KEY_ARCHIVE_START, undef, $strArchiveStart);\n"
|
|
"$oBackupManifest->set(MANIFEST_SECTION_BACKUP, MANIFEST_KEY_LSN_START, undef, $strLsnStart);\n"
|
|
"&log(INFO, \"backup start archive = ${strArchiveStart}, lsn = ${strLsnStart}\");\n"
|
|
"\n\n"
|
|
"$strTimelineCurrent = substr($strArchiveStart, 0, 8);\n"
|
|
"\n\n"
|
|
"$hTablespaceMap = $oDbMaster->tablespaceMapGet();\n"
|
|
"\n\n"
|
|
"$hDatabaseMap = $oDbMaster->databaseMapGet();\n"
|
|
"\n\n"
|
|
"if (cfgOption(CFGOPT_BACKUP_STANDBY))\n"
|
|
"{\n"
|
|
"my ($strStandbyDbVersion, $iStandbyControlVersion, $iStandbyCatalogVersion, $ullStandbyDbSysId) = $oDbStandby->info();\n"
|
|
"$oBackupInfo->check($strStandbyDbVersion, $iStandbyControlVersion, $iStandbyCatalogVersion, $ullStandbyDbSysId);\n"
|
|
"\n"
|
|
"$oDbStandby->configValidate();\n"
|
|
"\n"
|
|
"&log(INFO, \"wait for replay on the standby to reach ${strLsnStart}\");\n"
|
|
"\n"
|
|
"my ($strReplayedLSN, $strCheckpointLSN) = $oDbStandby->replayWait($strLsnStart);\n"
|
|
"\n"
|
|
"&log(\n"
|
|
"INFO,\n"
|
|
"\"replay on the standby reached ${strReplayedLSN}\" .\n"
|
|
"(defined($strCheckpointLSN) ? \", checkpoint ${strCheckpointLSN}\" : ''));\n"
|
|
"\n\n"
|
|
"undef($oDbStandby);\n"
|
|
"protocolDestroy(CFGOPTVAL_REMOTE_TYPE_DB, $self->{iCopyRemoteIdx}, true);\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n\n"
|
|
"if ($strType ne CFGOPTVAL_BACKUP_TYPE_FULL && defined($strBackupLastPath))\n"
|
|
"{\n"
|
|
"\n\n"
|
|
"if (!$oLastManifest->test(MANIFEST_SECTION_BACKUP_OPTION, MANIFEST_KEY_CHECKSUM_PAGE))\n"
|
|
"{\n"
|
|
"cfgOptionSet(CFGOPT_CHECKSUM_PAGE, false);\n"
|
|
"}\n"
|
|
"else\n"
|
|
"{\n"
|
|
"my $bChecksumPageLast =\n"
|
|
"$oLastManifest->boolGet(MANIFEST_SECTION_BACKUP_OPTION, MANIFEST_KEY_CHECKSUM_PAGE);\n"
|
|
"\n"
|
|
"if ($bChecksumPageLast != cfgOption(CFGOPT_CHECKSUM_PAGE))\n"
|
|
"{\n"
|
|
"&log(WARN,\n"
|
|
"\"${strType} backup cannot alter '\" . cfgOptionName(CFGOPT_CHECKSUM_PAGE) . \"' option to '\" .\n"
|
|
"boolFormat(cfgOption(CFGOPT_CHECKSUM_PAGE)) . \"', reset to '\" . boolFormat($bChecksumPageLast) .\n"
|
|
"\"' from ${strBackupLastPath}\");\n"
|
|
"cfgOptionSet(CFGOPT_CHECKSUM_PAGE, $bChecksumPageLast);\n"
|
|
"}\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"$oBackupManifest->boolSet(MANIFEST_SECTION_BACKUP_OPTION, MANIFEST_KEY_CHECKSUM_PAGE, undef, cfgOption(CFGOPT_CHECKSUM_PAGE));\n"
|
|
"\n\n"
|
|
"cfgOptionSet(CFGOPT_DELTA, $oBackupManifest->build(\n"
|
|
"$oStorageDbMaster, $strDbMasterPath, $oLastManifest, cfgOption(CFGOPT_ONLINE), cfgOption(CFGOPT_DELTA), $hTablespaceMap,\n"
|
|
"$hDatabaseMap, cfgOption(CFGOPT_EXCLUDE, false), $strTimelineCurrent, $strTimelineLast));\n"
|
|
"\n"
|
|
"&log(TEST, TEST_MANIFEST_BUILD);\n"
|
|
"\n\n"
|
|
"if (defined($oAbortedManifest))\n"
|
|
"{\n"
|
|
"&log(WARN, \"aborted backup ${strBackupLabel} of same type exists, will be cleaned to remove invalid files and resumed\");\n"
|
|
"&log(TEST, TEST_BACKUP_RESUME);\n"
|
|
"\n\n\n"
|
|
"cfgOptionSet(CFGOPT_DELTA, $self->resumeClean($oStorageRepo, $strBackupLabel, $oBackupManifest, $oAbortedManifest,\n"
|
|
"cfgOption(CFGOPT_ONLINE), cfgOption(CFGOPT_DELTA), $strTimelineCurrent, $strTimelineAborted));\n"
|
|
"}\n"
|
|
"\n"
|
|
"else\n"
|
|
"{\n"
|
|
"logDebugMisc($strOperation, \"create backup path ${strBackupPath}\");\n"
|
|
"$oStorageRepo->pathCreate(STORAGE_REPO_BACKUP . \"/${strBackupLabel}\");\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"$oBackupManifest->boolSet(MANIFEST_SECTION_BACKUP_OPTION, MANIFEST_KEY_DELTA, undef, cfgOption(CFGOPT_DELTA));\n"
|
|
"\n\n"
|
|
"$oBackupManifest->saveCopy();\n"
|
|
"\n\n"
|
|
"my $lBackupSizeTotal =\n"
|
|
"$self->processManifest(\n"
|
|
"$strDbMasterPath, $strDbCopyPath, $strType, $strDbVersion, $bCompress, $bHardLink, $oBackupManifest, $strBackupLabel,\n"
|
|
"$strLsnStart);\n"
|
|
"&log(INFO, \"${strType} backup size = \" . fileSizeFormat($lBackupSizeTotal));\n"
|
|
"\n\n"
|
|
"undef($oStorageDbMaster);\n"
|
|
"\n\n"
|
|
"my $strArchiveStop = undef;\n"
|
|
"my $strLsnStop = undef;\n"
|
|
"\n"
|
|
"if (cfgOption(CFGOPT_ONLINE))\n"
|
|
"{\n"
|
|
"($strArchiveStop, $strLsnStop, my $strTimestampDbStop, my $oFileHash) = $oDbMaster->backupStop();\n"
|
|
"\n"
|
|
"$oBackupManifest->set(MANIFEST_SECTION_BACKUP, MANIFEST_KEY_ARCHIVE_STOP, undef, $strArchiveStop);\n"
|
|
"$oBackupManifest->set(MANIFEST_SECTION_BACKUP, MANIFEST_KEY_LSN_STOP, undef, $strLsnStop);\n"
|
|
"&log(INFO, \"backup stop archive = ${strArchiveStop}, lsn = ${strLsnStop}\");\n"
|
|
"\n\n"
|
|
"foreach my $strFile (sort(keys(%{$oFileHash})))\n"
|
|
"{\n"
|
|
"\n"
|
|
"if (defined($oFileHash->{$strFile}))\n"
|
|
"{\n"
|
|
"my $rhyFilter = [{strClass => STORAGE_FILTER_SHA}];\n"
|
|
"\n\n"
|
|
"if ($bCompress)\n"
|
|
"{\n"
|
|
"push(@{$rhyFilter}, {strClass => STORAGE_FILTER_GZIP});\n"
|
|
"}\n"
|
|
"\n\n\n"
|
|
"my $oDestinationFileIo = $oStorageRepo->openWrite(\n"
|
|
"STORAGE_REPO_BACKUP . \"/${strBackupLabel}/${strFile}\" . ($bCompress ? qw{.} . COMPRESS_EXT : ''),\n"
|
|
"{rhyFilter => $rhyFilter,\n"
|
|
"strCipherPass => defined($strCipherPassBackupSet) ? $strCipherPassBackupSet : undef});\n"
|
|
"\n\n"
|
|
"$oStorageRepo->put($oDestinationFileIo, $oFileHash->{$strFile});\n"
|
|
"\n\n"
|
|
"$oBackupManifest->fileAdd(\n"
|
|
"$strFile, time(), length($oFileHash->{$strFile}), $oDestinationFileIo->result(STORAGE_FILTER_SHA), true);\n"
|
|
"\n"
|
|
"&log(DETAIL, \"wrote '${strFile}' file returned from pg_stop_backup()\");\n"
|
|
"}\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"&log(TEST, TEST_BACKUP_STOP);\n"
|
|
"\n"
|
|
"undef($oDbMaster);\n"
|
|
"protocolDestroy(undef, undef, true);\n"
|
|
"\n\n\n\n"
|
|
"if (cfgOption(CFGOPT_ONLINE) && cfgOption(CFGOPT_ARCHIVE_CHECK))\n"
|
|
"{\n"
|
|
"\n"
|
|
"$oBackupManifest->saveCopy();\n"
|
|
"\n\n"
|
|
"my $lModificationTime = time();\n"
|
|
"\n\n"
|
|
"logDebugMisc($strOperation, \"retrieve archive logs ${strArchiveStart}:${strArchiveStop}\");\n"
|
|
"\n"
|
|
"my $oArchiveInfo = new pgBackRest::Archive::Info(storageRepo()->pathGet(STORAGE_REPO_ARCHIVE), true);\n"
|
|
"my $strArchiveId = $oArchiveInfo->archiveId();\n"
|
|
"my @stryArchive = lsnFileRange($strLsnStart, $strLsnStop, $strDbVersion, $iWalSegmentSize);\n"
|
|
"\n"
|
|
"foreach my $strArchive (@stryArchive)\n"
|
|
"{\n"
|
|
"my $strArchiveFile = walSegmentFind(\n"
|
|
"$oStorageRepo, $strArchiveId, substr($strArchiveStop, 0, 8) . $strArchive, cfgOption(CFGOPT_ARCHIVE_TIMEOUT));\n"
|
|
"\n"
|
|
"$strArchive = substr($strArchiveFile, 0, 24);\n"
|
|
"\n"
|
|
"if (cfgOption(CFGOPT_ARCHIVE_COPY))\n"
|
|
"{\n"
|
|
"logDebugMisc($strOperation, \"archive: ${strArchive} (${strArchiveFile})\");\n"
|
|
"\n\n"
|
|
"my $bArchiveCompressed = $strArchiveFile =~ ('^.*\\.' . COMPRESS_EXT . '\\$');\n"
|
|
"\n"
|
|
"$oStorageRepo->copy(\n"
|
|
"$oStorageRepo->openRead(STORAGE_REPO_ARCHIVE . \"/${strArchiveId}/${strArchiveFile}\",\n"
|
|
"{strCipherPass => $oArchiveInfo->cipherPassSub()}),\n"
|
|
"$oStorageRepo->openWrite(STORAGE_REPO_BACKUP . \"/${strBackupLabel}/\" . MANIFEST_TARGET_PGDATA . qw{/} .\n"
|
|
"$oBackupManifest->walPath() . \"/${strArchive}\" . ($bCompress ? qw{.} . COMPRESS_EXT : ''),\n"
|
|
"{bPathCreate => true, strCipherPass => $strCipherPassBackupSet})\n"
|
|
");\n"
|
|
"\n\n"
|
|
"my $strPathLog = MANIFEST_TARGET_PGDATA . qw{/} . $oBackupManifest->walPath();\n"
|
|
"my $strFileLog = \"${strPathLog}/${strArchive}\";\n"
|
|
"\n\n"
|
|
"$oBackupManifest->fileAdd(\n"
|
|
"$strFileLog, $lModificationTime, PG_WAL_SEGMENT_SIZE, substr($strArchiveFile, 25, 40), true);\n"
|
|
"}\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"my $lTimestampStop = time();\n"
|
|
"$oBackupManifest->set(MANIFEST_SECTION_BACKUP, MANIFEST_KEY_TIMESTAMP_STOP, undef, $lTimestampStop + 0);\n"
|
|
"$oBackupManifest->set(MANIFEST_SECTION_BACKUP, MANIFEST_KEY_LABEL, undef, $strBackupLabel);\n"
|
|
"\n\n"
|
|
"if ($oStorageRepo->driver()->capability(STORAGE_CAPABILITY_PATH_SYNC))\n"
|
|
"{\n"
|
|
"\n"
|
|
"$oStorageRepo->pathSync(STORAGE_REPO_BACKUP . \"/${strBackupLabel}\");\n"
|
|
"\n"
|
|
"foreach my $strPath ($oBackupManifest->keys(MANIFEST_SECTION_TARGET_PATH))\n"
|
|
"{\n"
|
|
"my $strPathSync = $oStorageRepo->pathGet(STORAGE_REPO_BACKUP . \"/${strBackupLabel}/$strPath\");\n"
|
|
"\n\n"
|
|
"if ($strType eq CFGOPTVAL_BACKUP_TYPE_FULL || $oStorageRepo->pathExists($strPathSync))\n"
|
|
"{\n"
|
|
"$oStorageRepo->pathSync($strPathSync);\n"
|
|
"}\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"$oBackupManifest->save();\n"
|
|
"\n"
|
|
"&log(INFO, \"new backup label = ${strBackupLabel}\");\n"
|
|
"\n\n\n"
|
|
"my $strHistoryPath = $oStorageRepo->pathGet(\n"
|
|
"STORAGE_REPO_BACKUP . qw{/} . PATH_BACKUP_HISTORY . qw{/} . substr($strBackupLabel, 0, 4));\n"
|
|
"\n"
|
|
"$oStorageRepo->copy(\n"
|
|
"$oStorageRepo->openRead(STORAGE_REPO_BACKUP . \"/${strBackupLabel}/\" . FILE_MANIFEST,\n"
|
|
"{'strCipherPass' => $strCipherPassManifest}),\n"
|
|
"$oStorageRepo->openWrite(\n"
|
|
"\"${strHistoryPath}/${strBackupLabel}.manifest.\" . COMPRESS_EXT,\n"
|
|
"{rhyFilter => [{strClass => STORAGE_FILTER_GZIP}],\n"
|
|
"bPathCreate => true, bAtomic => true,\n"
|
|
"strCipherPass => defined($strCipherPassManifest) ? $strCipherPassManifest : undef}));\n"
|
|
"\n\n"
|
|
"if ($oStorageRepo->driver()->capability(STORAGE_CAPABILITY_PATH_SYNC))\n"
|
|
"{\n"
|
|
"$oStorageRepo->pathSync(STORAGE_REPO_BACKUP . qw{/} . PATH_BACKUP_HISTORY);\n"
|
|
"$oStorageRepo->pathSync($strHistoryPath);\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"$oStorageRepo->remove(STORAGE_REPO_BACKUP . qw(/) . LINK_LATEST);\n"
|
|
"\n"
|
|
"if (storageRepo()->driver()->capability(STORAGE_CAPABILITY_LINK))\n"
|
|
"{\n"
|
|
"$oStorageRepo->linkCreate(\n"
|
|
"STORAGE_REPO_BACKUP . \"/${strBackupLabel}\", STORAGE_REPO_BACKUP . qw{/} . LINK_LATEST, {bRelative => true});\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"$oBackupInfo->add($oBackupManifest);\n"
|
|
"\n\n"
|
|
"if ($oStorageRepo->driver()->capability(STORAGE_CAPABILITY_PATH_SYNC))\n"
|
|
"{\n"
|
|
"$oStorageRepo->pathSync(STORAGE_REPO_BACKUP);\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"return logDebugReturn($strOperation);\n"
|
|
"}\n"
|
|
"\n"
|
|
"1;\n"
|
|
},
|
|
{
|
|
.name = "pgBackRest/Backup/Common.pm",
|
|
.data =
|
|
"\n\n\n"
|
|
"package pgBackRest::Backup::Common;\n"
|
|
"\n"
|
|
"use strict;\n"
|
|
"use warnings FATAL => qw(all);\n"
|
|
"use Carp qw(confess);\n"
|
|
"\n"
|
|
"use Exporter qw(import);\n"
|
|
"our @EXPORT = qw();\n"
|
|
"use File::Basename;\n"
|
|
"\n"
|
|
"use pgBackRest::Common::Log;\n"
|
|
"use pgBackRest::Common::String;\n"
|
|
"use pgBackRest::Common::Wait;\n"
|
|
"use pgBackRest::Config::Config;\n"
|
|
"use pgBackRest::Protocol::Storage::Helper;\n"
|
|
"use pgBackRest::Storage::Helper;\n"
|
|
"use pgBackRest::Manifest;\n"
|
|
"\n\n\n\n"
|
|
"use constant LINK_LATEST => 'latest';\n"
|
|
"push @EXPORT, qw(LINK_LATEST);\n"
|
|
"\n\n\n\n\n\n"
|
|
"sub backupRegExpGet\n"
|
|
"{\n"
|
|
"\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$bFull,\n"
|
|
"$bDifferential,\n"
|
|
"$bIncremental,\n"
|
|
"$bAnchor\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '::backupRegExpGet', \\@_,\n"
|
|
"{name => 'bFull', default => false},\n"
|
|
"{name => 'bDifferential', default => false},\n"
|
|
"{name => 'bIncremental', default => false},\n"
|
|
"{name => 'bAnchor', default => true}\n"
|
|
");\n"
|
|
"\n\n"
|
|
"if (!($bFull || $bDifferential || $bIncremental))\n"
|
|
"{\n"
|
|
"confess &log(ASSERT, 'at least one backup type must be selected');\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"my $strDateTimeRegExp = \"[0-9]{8}\\\\-[0-9]{6}\";\n"
|
|
"\n"
|
|
"my $strRegExp = ($bAnchor ? '^' : '') . $strDateTimeRegExp . 'F';\n"
|
|
"\n\n"
|
|
"if ($bDifferential || $bIncremental)\n"
|
|
"{\n"
|
|
"\n"
|
|
"if ($bFull)\n"
|
|
"{\n"
|
|
"$strRegExp .= \"(\\\\_\";\n"
|
|
"}\n"
|
|
"\n"
|
|
"else\n"
|
|
"{\n"
|
|
"$strRegExp .= \"\\\\_\";\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"$strRegExp .= $strDateTimeRegExp;\n"
|
|
"\n\n"
|
|
"if ($bDifferential && $bIncremental)\n"
|
|
"{\n"
|
|
"$strRegExp .= '(D|I)';\n"
|
|
"}\n"
|
|
"\n"
|
|
"elsif ($bDifferential)\n"
|
|
"{\n"
|
|
"$strRegExp .= 'D';\n"
|
|
"}\n"
|
|
"\n"
|
|
"else\n"
|
|
"{\n"
|
|
"$strRegExp .= 'I';\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if ($bFull)\n"
|
|
"{\n"
|
|
"$strRegExp .= '){0,1}';\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"$strRegExp .= $bAnchor ? \"\\$\" : '';\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'strRegExp', value => $strRegExp}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n"
|
|
"push @EXPORT, qw(backupRegExpGet);\n"
|
|
"\n\n\n\n\n\n"
|
|
"sub backupLabelFormat\n"
|
|
"{\n"
|
|
"\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$strType,\n"
|
|
"$strBackupLabelLast,\n"
|
|
"$lTimestampStart\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '::backupLabelFormat', \\@_,\n"
|
|
"{name => 'strType', trace => true},\n"
|
|
"{name => 'strBackupLabelLast', required => false, trace => true},\n"
|
|
"{name => 'lTimestampTart', trace => true}\n"
|
|
");\n"
|
|
"\n\n"
|
|
"my $strBackupLabel;\n"
|
|
"\n"
|
|
"if ($strType eq CFGOPTVAL_BACKUP_TYPE_FULL)\n"
|
|
"{\n"
|
|
"\n"
|
|
"if (defined($strBackupLabelLast))\n"
|
|
"{\n"
|
|
"confess &log(ASSERT, \"strBackupLabelLast must not be defined when strType = '${strType}'\");\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"$strBackupLabel = timestampFileFormat(undef, $lTimestampStart) . 'F';\n"
|
|
"}\n"
|
|
"\n"
|
|
"else\n"
|
|
"{\n"
|
|
"\n"
|
|
"if (!defined($strBackupLabelLast))\n"
|
|
"{\n"
|
|
"confess &log(ASSERT, \"strBackupLabelLast must be defined when strType = '${strType}'\");\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"$strBackupLabel = substr($strBackupLabelLast, 0, 16);\n"
|
|
"\n\n"
|
|
"$strBackupLabel .= '_' . timestampFileFormat(undef, $lTimestampStart);\n"
|
|
"\n\n"
|
|
"if ($strType eq CFGOPTVAL_BACKUP_TYPE_DIFF)\n"
|
|
"{\n"
|
|
"$strBackupLabel .= 'D';\n"
|
|
"}\n"
|
|
"\n"
|
|
"else\n"
|
|
"{\n"
|
|
"$strBackupLabel .= 'I';\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'strBackupLabel', value => $strBackupLabel, trace => true}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n"
|
|
"push @EXPORT, qw(backupLabelFormat);\n"
|
|
"\n\n\n\n\n\n"
|
|
"sub backupLabel\n"
|
|
"{\n"
|
|
"\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$oStorageRepo,\n"
|
|
"$strType,\n"
|
|
"$strBackupLabelLast,\n"
|
|
"$lTimestampStart\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '::backupLabelFormat', \\@_,\n"
|
|
"{name => 'oStorageRepo', trace => true},\n"
|
|
"{name => 'strType', trace => true},\n"
|
|
"{name => 'strBackupLabelLast', required => false, trace => true},\n"
|
|
"{name => 'lTimestampStart', trace => true}\n"
|
|
");\n"
|
|
"\n\n"
|
|
"my $strBackupLabel = backupLabelFormat($strType, $strBackupLabelLast, $lTimestampStart);\n"
|
|
"\n\n\n\n\n"
|
|
"if ($oStorageRepo->list(\n"
|
|
"STORAGE_REPO_BACKUP,\n"
|
|
"{strExpression =>\n"
|
|
"($strType eq CFGOPTVAL_BACKUP_TYPE_FULL ? '^' : '_') . timestampFileFormat(undef, $lTimestampStart) .\n"
|
|
"($strType eq CFGOPTVAL_BACKUP_TYPE_FULL ? 'F' : '(D|I)$')}) ||\n"
|
|
"$oStorageRepo->list(\n"
|
|
"STORAGE_REPO_BACKUP . qw{/} . PATH_BACKUP_HISTORY . '/' . timestampFormat('%4d', $lTimestampStart),\n"
|
|
"{strExpression =>\n"
|
|
"($strType eq CFGOPTVAL_BACKUP_TYPE_FULL ? '^' : '_') . timestampFileFormat(undef, $lTimestampStart) .\n"
|
|
"($strType eq CFGOPTVAL_BACKUP_TYPE_FULL ? 'F' : '(D|I)\\.manifest\\.' . COMPRESS_EXT . qw{$}),\n"
|
|
"bIgnoreMissing => true}))\n"
|
|
"{\n"
|
|
"waitRemainder();\n"
|
|
"$strBackupLabel = backupLabelFormat($strType, $strBackupLabelLast, time());\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'strBackupLabel', value => $strBackupLabel, trace => true}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n"
|
|
"push @EXPORT, qw(backupLabel);\n"
|
|
"\n"
|
|
"1;\n"
|
|
},
|
|
{
|
|
.name = "pgBackRest/Backup/File.pm",
|
|
.data =
|
|
"\n\n\n"
|
|
"package pgBackRest::Backup::File;\n"
|
|
"\n"
|
|
"use strict;\n"
|
|
"use warnings FATAL => qw(all);\n"
|
|
"use Carp qw(confess);\n"
|
|
"\n"
|
|
"use Exporter qw(import);\n"
|
|
"our @EXPORT = qw();\n"
|
|
"use File::Basename qw(dirname);\n"
|
|
"use Storable qw(dclone);\n"
|
|
"\n"
|
|
"use pgBackRest::Backup::Filter::PageChecksum;\n"
|
|
"use pgBackRest::Common::Exception;\n"
|
|
"use pgBackRest::Common::Io::Handle;\n"
|
|
"use pgBackRest::Common::Log;\n"
|
|
"use pgBackRest::Common::String;\n"
|
|
"use pgBackRest::DbVersion;\n"
|
|
"use pgBackRest::Manifest;\n"
|
|
"use pgBackRest::Protocol::Storage::Helper;\n"
|
|
"use pgBackRest::Storage::Base;\n"
|
|
"use pgBackRest::Storage::Filter::Gzip;\n"
|
|
"use pgBackRest::Storage::Filter::Sha;\n"
|
|
"use pgBackRest::Storage::Helper;\n"
|
|
"\n\n\n\n"
|
|
"use constant BACKUP_FILE_CHECKSUM => 0;\n"
|
|
"push @EXPORT, qw(BACKUP_FILE_CHECKSUM);\n"
|
|
"use constant BACKUP_FILE_COPY => 1;\n"
|
|
"push @EXPORT, qw(BACKUP_FILE_COPY);\n"
|
|
"use constant BACKUP_FILE_RECOPY => 2;\n"
|
|
"push @EXPORT, qw(BACKUP_FILE_RECOPY);\n"
|
|
"use constant BACKUP_FILE_SKIP => 3;\n"
|
|
"push @EXPORT, qw(BACKUP_FILE_SKIP);\n"
|
|
"use constant BACKUP_FILE_NOOP => 4;\n"
|
|
"push @EXPORT, qw(BACKUP_FILE_NOOP);\n"
|
|
"\n\n\n\n"
|
|
"sub backupFile\n"
|
|
"{\n"
|
|
"\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$strDbFile,\n"
|
|
"$strRepoFile,\n"
|
|
"$lSizeFile,\n"
|
|
"$strChecksum,\n"
|
|
"$bChecksumPage,\n"
|
|
"$strBackupLabel,\n"
|
|
"$bCompress,\n"
|
|
"$iCompressLevel,\n"
|
|
"$lModificationTime,\n"
|
|
"$bIgnoreMissing,\n"
|
|
"$hExtraParam,\n"
|
|
"$bDelta,\n"
|
|
"$bHasReference,\n"
|
|
"$strCipherPass,\n"
|
|
"\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '::backupFile', \\@_,\n"
|
|
"{name => 'strDbFile', trace => true},\n"
|
|
"{name => 'strRepoFile', trace => true},\n"
|
|
"{name => 'lSizeFile', trace => true},\n"
|
|
"{name => 'strChecksum', required => false, trace => true},\n"
|
|
"{name => 'bChecksumPage', trace => true},\n"
|
|
"{name => 'strBackupLabel', trace => true},\n"
|
|
"{name => 'bCompress', trace => true},\n"
|
|
"{name => 'iCompressLevel', trace => true},\n"
|
|
"{name => 'lModificationTime', trace => true},\n"
|
|
"{name => 'bIgnoreMissing', default => true, trace => true},\n"
|
|
"{name => 'hExtraParam', required => false, trace => true},\n"
|
|
"{name => 'bDelta', trace => true},\n"
|
|
"{name => 'bHasReference', trace => true},\n"
|
|
"{name => 'strCipherPass', required => false, trace => true},\n"
|
|
");\n"
|
|
"\n"
|
|
"my $oStorageRepo = storageRepo();\n"
|
|
"my $iCopyResult = BACKUP_FILE_COPY;\n"
|
|
"my $strCopyChecksum;\n"
|
|
"my $rExtra;\n"
|
|
"my $lCopySize;\n"
|
|
"my $lRepoSize;\n"
|
|
"\n\n"
|
|
"my $strFileOp = $strRepoFile . ($bCompress ? '.' . COMPRESS_EXT : '');\n"
|
|
"\n"
|
|
"my $bCopy = true;\n"
|
|
"\n\n\n"
|
|
"if (defined($strChecksum))\n"
|
|
"{\n"
|
|
"\n"
|
|
"if ($bDelta)\n"
|
|
"{\n"
|
|
"($strCopyChecksum, $lCopySize) = storageDb()->hashSize($strDbFile, {bIgnoreMissing => $bIgnoreMissing});\n"
|
|
"\n\n"
|
|
"if (defined($strCopyChecksum))\n"
|
|
"{\n"
|
|
"$bCopy = !($strCopyChecksum eq $strChecksum && $lCopySize == $lSizeFile);\n"
|
|
"\n\n\n\n"
|
|
"if (!$bCopy && $bHasReference)\n"
|
|
"{\n"
|
|
"$iCopyResult = BACKUP_FILE_NOOP;\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n"
|
|
"else\n"
|
|
"{\n"
|
|
"$iCopyResult = BACKUP_FILE_SKIP;\n"
|
|
"$bCopy = false;\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"if (!$bDelta || !$bHasReference)\n"
|
|
"{\n"
|
|
"\n\n"
|
|
"if ($iCopyResult == BACKUP_FILE_SKIP)\n"
|
|
"{\n"
|
|
"$oStorageRepo->remove(STORAGE_REPO_BACKUP . \"/${strBackupLabel}/${strFileOp}\");\n"
|
|
"}\n"
|
|
"elsif (!$bDelta || !$bCopy)\n"
|
|
"{\n"
|
|
"\n"
|
|
"my $rhyFilter;\n"
|
|
"\n"
|
|
"if ($bCompress)\n"
|
|
"{\n"
|
|
"push(@{$rhyFilter}, {strClass => STORAGE_FILTER_GZIP, rxyParam => [{strCompressType => STORAGE_DECOMPRESS}]});\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"($strCopyChecksum, $lCopySize) = $oStorageRepo->hashSize(\n"
|
|
"$oStorageRepo->openRead(STORAGE_REPO_BACKUP . \"/${strBackupLabel}/${strFileOp}\",\n"
|
|
"{rhyFilter => $rhyFilter, strCipherPass => $strCipherPass}));\n"
|
|
"\n\n"
|
|
"$bCopy = !($strCopyChecksum eq $strChecksum && $lCopySize == $lSizeFile);\n"
|
|
"\n\n"
|
|
"$iCopyResult = $bCopy ? BACKUP_FILE_RECOPY : BACKUP_FILE_CHECKSUM;\n"
|
|
"}\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if ($bCopy)\n"
|
|
"{\n"
|
|
"\n"
|
|
"my $rhyFilter = [{strClass => STORAGE_FILTER_SHA}];\n"
|
|
"\n\n"
|
|
"if ($bChecksumPage)\n"
|
|
"{\n"
|
|
"\n"
|
|
"my $iSegmentNo = ($strDbFile =~ /\\.[0-9]+$/) ? substr(($strDbFile =~ m/\\.[0-9]+$/g)[0], 1) + 0 : 0;\n"
|
|
"\n"
|
|
"push(\n"
|
|
"@{$rhyFilter},\n"
|
|
"{strClass => BACKUP_FILTER_PAGECHECKSUM,\n"
|
|
"rxyParam => [$iSegmentNo, $hExtraParam->{iWalId}, $hExtraParam->{iWalOffset}]});\n"
|
|
"};\n"
|
|
"\n\n"
|
|
"if ($bCompress)\n"
|
|
"{\n"
|
|
"push(@{$rhyFilter}, {strClass => STORAGE_FILTER_GZIP, rxyParam => [{iLevel => $iCompressLevel}]});\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"my $oSourceFileIo = storageDb()->openRead($strDbFile, {rhyFilter => $rhyFilter, bIgnoreMissing => $bIgnoreMissing});\n"
|
|
"\n\n"
|
|
"if (defined($oSourceFileIo))\n"
|
|
"{\n"
|
|
"my $oDestinationFileIo = $oStorageRepo->openWrite(\n"
|
|
"STORAGE_REPO_BACKUP . \"/${strBackupLabel}/${strFileOp}\",\n"
|
|
"{bPathCreate => true, bProtocolCompress => !$bCompress, strCipherPass => $strCipherPass});\n"
|
|
"\n\n"
|
|
"$oStorageRepo->copy($oSourceFileIo, $oDestinationFileIo);\n"
|
|
"\n\n"
|
|
"$strCopyChecksum = $oSourceFileIo->result(STORAGE_FILTER_SHA);\n"
|
|
"$lCopySize = $oSourceFileIo->result(COMMON_IO_HANDLE);\n"
|
|
"$lRepoSize = $oDestinationFileIo->result(COMMON_IO_HANDLE);\n"
|
|
"\n"
|
|
"if (!defined($lRepoSize))\n"
|
|
"{\n"
|
|
"confess &log(ERROR, \"REPO_SIZE IS NOT SET\");\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"$rExtra = $bChecksumPage ? $oSourceFileIo->result(BACKUP_FILTER_PAGECHECKSUM) : undef;\n"
|
|
"}\n"
|
|
"\n"
|
|
"else\n"
|
|
"{\n"
|
|
"$iCopyResult = BACKUP_FILE_SKIP;\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n\n\n\n\n"
|
|
"if ((($iCopyResult == BACKUP_FILE_COPY || $iCopyResult == BACKUP_FILE_RECOPY) &&\n"
|
|
"$oStorageRepo->driver()->capability(STORAGE_CAPABILITY_SIZE_DIFF)) ||\n"
|
|
"$iCopyResult == BACKUP_FILE_CHECKSUM)\n"
|
|
"{\n"
|
|
"$lRepoSize = ($oStorageRepo->info(STORAGE_REPO_BACKUP . \"/${strBackupLabel}/${strFileOp}\"))->size();\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'iCopyResult', value => $iCopyResult, trace => true},\n"
|
|
"{name => 'lCopySize', value => $lCopySize, trace => true},\n"
|
|
"{name => 'lRepoSize', value => $lRepoSize, trace => true},\n"
|
|
"{name => 'strCopyChecksum', value => $strCopyChecksum, trace => true},\n"
|
|
"{name => 'rExtra', value => $rExtra, trace => true},\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n"
|
|
"push @EXPORT, qw(backupFile);\n"
|
|
"\n\n\n\n"
|
|
"sub backupManifestUpdate\n"
|
|
"{\n"
|
|
"\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$oManifest,\n"
|
|
"$strHost,\n"
|
|
"$iLocalId,\n"
|
|
"$strDbFile,\n"
|
|
"$strRepoFile,\n"
|
|
"$lSize,\n"
|
|
"$strChecksum,\n"
|
|
"$bChecksumPage,\n"
|
|
"$iCopyResult,\n"
|
|
"$lSizeCopy,\n"
|
|
"$lSizeRepo,\n"
|
|
"$strChecksumCopy,\n"
|
|
"$rExtra,\n"
|
|
"$lSizeTotal,\n"
|
|
"$lSizeCurrent,\n"
|
|
"$lManifestSaveSize,\n"
|
|
"$lManifestSaveCurrent\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '::backupManifestUpdate', \\@_,\n"
|
|
"{name => 'oManifest', trace => true},\n"
|
|
"{name => 'strHost', required => false, trace => true},\n"
|
|
"{name => 'iLocalId', required => false, trace => true},\n"
|
|
"\n\n"
|
|
"{name => 'strDbFile', trace => true},\n"
|
|
"{name => 'strRepoFile', trace => true},\n"
|
|
"{name => 'lSize', required => false, trace => true},\n"
|
|
"{name => 'strChecksum', required => false, trace => true},\n"
|
|
"{name => 'bChecksumPage', trace => true},\n"
|
|
"\n\n"
|
|
"{name => 'iCopyResult', trace => true},\n"
|
|
"{name => 'lSizeCopy', required => false, trace => true},\n"
|
|
"{name => 'lSizeRepo', required => false, trace => true},\n"
|
|
"{name => 'strChecksumCopy', required => false, trace => true},\n"
|
|
"{name => 'rExtra', required => false, trace => true},\n"
|
|
"\n\n"
|
|
"{name => 'lSizeTotal', trace => true},\n"
|
|
"{name => 'lSizeCurrent', trace => true},\n"
|
|
"{name => 'lManifestSaveSize', trace => true},\n"
|
|
"{name => 'lManifestSaveCurrent', trace => true}\n"
|
|
");\n"
|
|
"\n\n"
|
|
"$lSizeCurrent += $lSize;\n"
|
|
"\n\n"
|
|
"if ($iCopyResult == BACKUP_FILE_NOOP)\n"
|
|
"{\n"
|
|
"\n"
|
|
"$oManifest->numericSet(MANIFEST_SECTION_TARGET_FILE, $strRepoFile, MANIFEST_SUBKEY_SIZE, $lSizeCopy);\n"
|
|
"$oManifest->set(MANIFEST_SECTION_TARGET_FILE, $strRepoFile, MANIFEST_SUBKEY_CHECKSUM, $strChecksumCopy);\n"
|
|
"\n"
|
|
"&log(DETAIL,\n"
|
|
"'match file from prior backup ' . (defined($strHost) ? \"${strHost}:\" : '') . \"${strDbFile} (\" .\n"
|
|
"fileSizeFormat($lSizeCopy) . ', ' . int($lSizeCurrent * 100 / $lSizeTotal) . '%)' .\n"
|
|
"($lSizeCopy != 0 ? \" checksum ${strChecksumCopy}\" : ''),\n"
|
|
"undef, undef, undef, $iLocalId);\n"
|
|
"}\n"
|
|
"\n"
|
|
"else\n"
|
|
"{\n"
|
|
"\n"
|
|
"if ($iCopyResult == BACKUP_FILE_RECOPY)\n"
|
|
"{\n"
|
|
"&log(\n"
|
|
"WARN,\n"
|
|
"\"resumed backup file ${strRepoFile} does not have expected checksum ${strChecksum}. The file will be recopied and\" .\n"
|
|
"\" backup will continue but this may be an issue unless the resumed backup path in the repository is known to be\" .\n"
|
|
"\" corrupted.\\n\" .\n"
|
|
"\"NOTE: this does not indicate a problem with the PostgreSQL page checksums.\");\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if ($iCopyResult == BACKUP_FILE_COPY || $iCopyResult == BACKUP_FILE_RECOPY || $iCopyResult == BACKUP_FILE_CHECKSUM)\n"
|
|
"{\n"
|
|
"\n"
|
|
"&log($iCopyResult == BACKUP_FILE_CHECKSUM ? DETAIL : INFO,\n"
|
|
"($iCopyResult == BACKUP_FILE_CHECKSUM ?\n"
|
|
"'checksum resumed file ' : 'backup file ' . (defined($strHost) ? \"${strHost}:\" : '')) .\n"
|
|
"\"${strDbFile} (\" . fileSizeFormat($lSizeCopy) .\n"
|
|
"', ' . int($lSizeCurrent * 100 / $lSizeTotal) . '%)' .\n"
|
|
"($lSizeCopy != 0 ? \" checksum ${strChecksumCopy}\" : ''), undef, undef, undef, $iLocalId);\n"
|
|
"\n"
|
|
"$oManifest->numericSet(MANIFEST_SECTION_TARGET_FILE, $strRepoFile, MANIFEST_SUBKEY_SIZE, $lSizeCopy);\n"
|
|
"\n"
|
|
"if ($lSizeRepo != $lSizeCopy)\n"
|
|
"{\n"
|
|
"$oManifest->numericSet(MANIFEST_SECTION_TARGET_FILE, $strRepoFile, MANIFEST_SUBKEY_REPO_SIZE, $lSizeRepo);\n"
|
|
"}\n"
|
|
"\n"
|
|
"if ($lSizeCopy > 0)\n"
|
|
"{\n"
|
|
"$oManifest->set(MANIFEST_SECTION_TARGET_FILE, $strRepoFile, MANIFEST_SUBKEY_CHECKSUM, $strChecksumCopy);\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if ($iCopyResult == BACKUP_FILE_COPY || $iCopyResult == BACKUP_FILE_RECOPY)\n"
|
|
"{\n"
|
|
"$oManifest->remove(MANIFEST_SECTION_TARGET_FILE, $strRepoFile, MANIFEST_SUBKEY_REFERENCE);\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if ($bChecksumPage)\n"
|
|
"{\n"
|
|
"\n"
|
|
"if (defined($rExtra->{bValid}))\n"
|
|
"{\n"
|
|
"\n"
|
|
"$oManifest->boolSet(MANIFEST_SECTION_TARGET_FILE, $strRepoFile, MANIFEST_SUBKEY_CHECKSUM_PAGE, $rExtra->{bValid});\n"
|
|
"\n\n"
|
|
"if (!$rExtra->{bValid})\n"
|
|
"{\n"
|
|
"\n"
|
|
"if ($lSizeCopy % PG_PAGE_SIZE != 0)\n"
|
|
"{\n"
|
|
"\n"
|
|
"if (!defined($rExtra->{bAlign}) || $rExtra->{bAlign})\n"
|
|
"{\n"
|
|
"confess &log(ASSERT, 'bAlign flag should have been set for misaligned page');\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"&log(WARN,\n"
|
|
"'page misalignment in file ' . (defined($strHost) ? \"${strHost}:\" : '') .\n"
|
|
"\"${strDbFile}: file size ${lSizeCopy} is not divisible by page size \" . PG_PAGE_SIZE);\n"
|
|
"}\n"
|
|
"\n"
|
|
"else\n"
|
|
"{\n"
|
|
"$oManifest->set(\n"
|
|
"MANIFEST_SECTION_TARGET_FILE, $strRepoFile, MANIFEST_SUBKEY_CHECKSUM_PAGE_ERROR,\n"
|
|
"dclone($rExtra->{iyPageError}));\n"
|
|
"\n\n"
|
|
"my $strPageError;\n"
|
|
"my $iPageErrorTotal = 0;\n"
|
|
"\n"
|
|
"foreach my $iyPage (@{$rExtra->{iyPageError}})\n"
|
|
"{\n"
|
|
"$strPageError .= (defined($strPageError) ? ', ' : '');\n"
|
|
"\n\n"
|
|
"if (ref($iyPage))\n"
|
|
"{\n"
|
|
"$strPageError .= $$iyPage[0] . '-' . $$iyPage[1];\n"
|
|
"$iPageErrorTotal += ($$iyPage[1] - $$iyPage[0]) + 1;\n"
|
|
"}\n"
|
|
"\n"
|
|
"else\n"
|
|
"{\n"
|
|
"$strPageError .= $iyPage;\n"
|
|
"$iPageErrorTotal += 1;\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if ($iPageErrorTotal == 0)\n"
|
|
"{\n"
|
|
"confess &log(ASSERT, 'page checksum error list should have at least one entry');\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"&log(WARN,\n"
|
|
"'invalid page checksum' . ($iPageErrorTotal > 1 ? 's' : '') .\n"
|
|
"' found in file ' . (defined($strHost) ? \"${strHost}:\" : '') . \"${strDbFile} at page\" .\n"
|
|
"($iPageErrorTotal > 1 ? 's' : '') . \" ${strPageError}\");\n"
|
|
"}\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n"
|
|
"elsif (!$oManifest->test(MANIFEST_SECTION_TARGET_FILE, $strRepoFile, MANIFEST_SUBKEY_CHECKSUM_PAGE))\n"
|
|
"{\n"
|
|
"confess &log(ASSERT, \"${strDbFile} should have calculated page checksums\");\n"
|
|
"}\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n"
|
|
"elsif ($iCopyResult == BACKUP_FILE_SKIP)\n"
|
|
"{\n"
|
|
"&log(DETAIL, 'skip file removed by database ' . (defined($strHost) ? \"${strHost}:\" : '') . $strDbFile);\n"
|
|
"$oManifest->remove(MANIFEST_SECTION_TARGET_FILE, $strRepoFile);\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"$lManifestSaveCurrent += $lSize;\n"
|
|
"\n"
|
|
"if ($lManifestSaveCurrent >= $lManifestSaveSize)\n"
|
|
"{\n"
|
|
"$oManifest->saveCopy();\n"
|
|
"\n"
|
|
"logDebugMisc\n"
|
|
"(\n"
|
|
"$strOperation, 'save manifest',\n"
|
|
"{name => 'lManifestSaveSize', value => $lManifestSaveSize},\n"
|
|
"{name => 'lManifestSaveCurrent', value => $lManifestSaveCurrent}\n"
|
|
");\n"
|
|
"\n"
|
|
"$lManifestSaveCurrent = 0;\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'lSizeCurrent', value => $lSizeCurrent, trace => true},\n"
|
|
"{name => 'lManifestSaveCurrent', value => $lManifestSaveCurrent, trace => true},\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n"
|
|
"push @EXPORT, qw(backupManifestUpdate);\n"
|
|
"\n"
|
|
"1;\n"
|
|
},
|
|
{
|
|
.name = "pgBackRest/Backup/Filter/PageChecksum.pm",
|
|
.data =
|
|
"\n\n\n"
|
|
"package pgBackRest::Backup::Filter::PageChecksum;\n"
|
|
"use parent 'pgBackRest::Common::Io::Filter';\n"
|
|
"\n"
|
|
"use strict;\n"
|
|
"use warnings FATAL => qw(all);\n"
|
|
"use Carp qw(confess);\n"
|
|
"use English '-no_match_vars';\n"
|
|
"\n"
|
|
"use Exporter qw(import);\n"
|
|
"our @EXPORT = qw();\n"
|
|
"\n"
|
|
"use pgBackRest::Common::Exception;\n"
|
|
"use pgBackRest::Common::Log;\n"
|
|
"use pgBackRest::DbVersion qw(PG_PAGE_SIZE);\n"
|
|
"use pgBackRest::LibC qw(:checksum);\n"
|
|
"\n\n\n\n"
|
|
"use constant BACKUP_FILTER_PAGECHECKSUM => __PACKAGE__;\n"
|
|
"push @EXPORT, qw(BACKUP_FILTER_PAGECHECKSUM);\n"
|
|
"\n\n\n\n"
|
|
"sub new\n"
|
|
"{\n"
|
|
"my $class = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$oParent,\n"
|
|
"$iSegmentNo,\n"
|
|
"$iWalId,\n"
|
|
"$iWalOffset,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->new', \\@_,\n"
|
|
"{name => 'oParent', trace => true},\n"
|
|
"{name => 'iSegmentNo', trace => true},\n"
|
|
"{name => 'iWalId', trace => true},\n"
|
|
"{name => 'iWalOffset', trace => true},\n"
|
|
");\n"
|
|
"\n\n"
|
|
"my $self = $class->SUPER::new($oParent);\n"
|
|
"bless $self, $class;\n"
|
|
"\n\n"
|
|
"$self->{iSegmentNo} = $iSegmentNo;\n"
|
|
"$self->{iWalId} = $iWalId;\n"
|
|
"$self->{iWalOffset} = $iWalOffset;\n"
|
|
"\n\n"
|
|
"$self->{hResult}{bValid} = true;\n"
|
|
"$self->{hResult}{bAlign} = true;\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'self', value => $self}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub read\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"my $rtBuffer = shift;\n"
|
|
"my $iSize = shift;\n"
|
|
"\n\n"
|
|
"my $iActualSize = $self->parent()->read($rtBuffer, $iSize);\n"
|
|
"\n\n"
|
|
"if ($iActualSize > 0)\n"
|
|
"{\n"
|
|
"\n"
|
|
"if (!$self->{hResult}{bAlign} || ($iActualSize % PG_PAGE_SIZE != 0))\n"
|
|
"{\n"
|
|
"if (!$self->{hResult}{bAlign})\n"
|
|
"{\n"
|
|
"confess &log(ASSERT, \"should not be possible to see two misaligned blocks in a row\");\n"
|
|
"}\n"
|
|
"\n"
|
|
"$self->{hResult}{bValid} = false;\n"
|
|
"$self->{hResult}{bAlign} = false;\n"
|
|
"delete($self->{hResult}{iyPageError});\n"
|
|
"}\n"
|
|
"else\n"
|
|
"{\n"
|
|
"\n"
|
|
"my $iBlockOffset = int(($self->size() - $iActualSize) / PG_PAGE_SIZE) + ($self->{iSegmentNo} * 131072);\n"
|
|
"\n"
|
|
"if (!pageChecksumBufferTest(\n"
|
|
"$$rtBuffer, $iActualSize, $iBlockOffset, PG_PAGE_SIZE, $self->{iWalId},\n"
|
|
"$self->{iWalOffset}))\n"
|
|
"{\n"
|
|
"$self->{hResult}{bValid} = false;\n"
|
|
"\n\n\n"
|
|
"for (my $iBlockNo = 0; $iBlockNo < int($iActualSize / PG_PAGE_SIZE); $iBlockNo++)\n"
|
|
"{\n"
|
|
"my $iBlockNoStart = $iBlockOffset + $iBlockNo;\n"
|
|
"\n"
|
|
"if (!pageChecksumTest(\n"
|
|
"substr($$rtBuffer, $iBlockNo * PG_PAGE_SIZE, PG_PAGE_SIZE), $iBlockNoStart, PG_PAGE_SIZE,\n"
|
|
"$self->{iWalId}, $self->{iWalOffset}))\n"
|
|
"{\n"
|
|
"my $iLastIdx = defined($self->{hResult}{iyPageError}) ? @{$self->{hResult}{iyPageError}} - 1 : 0;\n"
|
|
"my $iyLast = defined($self->{hResult}{iyPageError}) ? $self->{hResult}{iyPageError}[$iLastIdx] : undef;\n"
|
|
"\n"
|
|
"if (!defined($iyLast) || (!ref($iyLast) && $iyLast != $iBlockNoStart - 1) ||\n"
|
|
"(ref($iyLast) && $iyLast->[1] != $iBlockNoStart - 1))\n"
|
|
"{\n"
|
|
"push(@{$self->{hResult}{iyPageError}}, $iBlockNoStart);\n"
|
|
"}\n"
|
|
"elsif (!ref($iyLast))\n"
|
|
"{\n"
|
|
"$self->{hResult}{iyPageError}[$iLastIdx] = undef;\n"
|
|
"push(@{$self->{hResult}{iyPageError}[$iLastIdx]}, $iyLast);\n"
|
|
"push(@{$self->{hResult}{iyPageError}[$iLastIdx]}, $iBlockNoStart);\n"
|
|
"}\n"
|
|
"else\n"
|
|
"{\n"
|
|
"$self->{hResult}{iyPageError}[$iLastIdx][1] = $iBlockNoStart;\n"
|
|
"}\n"
|
|
"}\n"
|
|
"}\n"
|
|
"}\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"return $iActualSize;\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub close\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n"
|
|
"if (defined($self->{hResult}))\n"
|
|
"{\n"
|
|
"\n"
|
|
"$self->resultSet(BACKUP_FILTER_PAGECHECKSUM, $self->{hResult});\n"
|
|
"\n\n"
|
|
"undef($self->{hResult});\n"
|
|
"\n\n"
|
|
"return $self->parent()->close();\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n"
|
|
"1;\n"
|
|
},
|
|
{
|
|
.name = "pgBackRest/Backup/Info.pm",
|
|
.data =
|
|
"\n\n\n"
|
|
"package pgBackRest::Backup::Info;\n"
|
|
"use parent 'pgBackRest::Common::Ini';\n"
|
|
"\n"
|
|
"use strict;\n"
|
|
"use warnings FATAL => qw(all);\n"
|
|
"use Carp qw(confess);\n"
|
|
"use English '-no_match_vars';\n"
|
|
"\n"
|
|
"use Exporter qw(import);\n"
|
|
"our @EXPORT = qw();\n"
|
|
"use File::Basename qw(dirname basename);\n"
|
|
"use File::stat;\n"
|
|
"\n"
|
|
"use pgBackRest::Archive::Info;\n"
|
|
"use pgBackRest::Backup::Common;\n"
|
|
"use pgBackRest::Common::Exception;\n"
|
|
"use pgBackRest::Common::Ini;\n"
|
|
"use pgBackRest::Common::Log;\n"
|
|
"use pgBackRest::Config::Config;\n"
|
|
"use pgBackRest::InfoCommon;\n"
|
|
"use pgBackRest::Manifest;\n"
|
|
"use pgBackRest::Protocol::Helper;\n"
|
|
"use pgBackRest::Protocol::Storage::Helper;\n"
|
|
"use pgBackRest::Storage::Helper;\n"
|
|
"\n\n\n\n"
|
|
"use constant FILE_BACKUP_INFO => 'backup.info';\n"
|
|
"push @EXPORT, qw(FILE_BACKUP_INFO);\n"
|
|
"\n\n\n\n"
|
|
"use constant INFO_BACKUP_SECTION_BACKUP => MANIFEST_SECTION_BACKUP;\n"
|
|
"push @EXPORT, qw(INFO_BACKUP_SECTION_BACKUP);\n"
|
|
"use constant INFO_BACKUP_SECTION_BACKUP_CURRENT => INFO_BACKUP_SECTION_BACKUP . ':current';\n"
|
|
"push @EXPORT, qw(INFO_BACKUP_SECTION_BACKUP_CURRENT);\n"
|
|
"\n"
|
|
"use constant INFO_BACKUP_KEY_ARCHIVE_CHECK => MANIFEST_KEY_ARCHIVE_CHECK;\n"
|
|
"push @EXPORT, qw(INFO_BACKUP_KEY_ARCHIVE_CHECK);\n"
|
|
"use constant INFO_BACKUP_KEY_ARCHIVE_COPY => MANIFEST_KEY_ARCHIVE_COPY;\n"
|
|
"push @EXPORT, qw(INFO_BACKUP_KEY_ARCHIVE_COPY);\n"
|
|
"use constant INFO_BACKUP_KEY_ARCHIVE_START => MANIFEST_KEY_ARCHIVE_START;\n"
|
|
"push @EXPORT, qw(INFO_BACKUP_KEY_ARCHIVE_START);\n"
|
|
"use constant INFO_BACKUP_KEY_ARCHIVE_STOP => MANIFEST_KEY_ARCHIVE_STOP;\n"
|
|
"push @EXPORT, qw(INFO_BACKUP_KEY_ARCHIVE_STOP);\n"
|
|
"use constant INFO_BACKUP_KEY_BACKUP_STANDBY => MANIFEST_KEY_BACKUP_STANDBY;\n"
|
|
"push @EXPORT, qw(INFO_BACKUP_KEY_BACKUP_STANDBY);\n"
|
|
"use constant INFO_BACKUP_KEY_BACKUP_REPO_SIZE => 'backup-info-repo-size';\n"
|
|
"push @EXPORT, qw(INFO_BACKUP_KEY_BACKUP_REPO_SIZE);\n"
|
|
"use constant INFO_BACKUP_KEY_BACKUP_REPO_SIZE_DELTA => 'backup-info-repo-size-delta';\n"
|
|
"push @EXPORT, qw(INFO_BACKUP_KEY_BACKUP_REPO_SIZE_DELTA);\n"
|
|
"use constant INFO_BACKUP_KEY_BACKUP_SIZE => 'backup-info-size';\n"
|
|
"push @EXPORT, qw(INFO_BACKUP_KEY_BACKUP_SIZE);\n"
|
|
"use constant INFO_BACKUP_KEY_BACKUP_SIZE_DELTA => 'backup-info-size-delta';\n"
|
|
"push @EXPORT, qw(INFO_BACKUP_KEY_BACKUP_SIZE_DELTA);\n"
|
|
"use constant INFO_BACKUP_KEY_CATALOG => MANIFEST_KEY_CATALOG;\n"
|
|
"push @EXPORT, qw(INFO_BACKUP_KEY_CATALOG);\n"
|
|
"use constant INFO_BACKUP_KEY_CONTROL => MANIFEST_KEY_CONTROL;\n"
|
|
"push @EXPORT, qw(INFO_BACKUP_KEY_CONTROL);\n"
|
|
"use constant INFO_BACKUP_KEY_COMPRESS => MANIFEST_KEY_COMPRESS;\n"
|
|
"push @EXPORT, qw(INFO_BACKUP_KEY_COMPRESS);\n"
|
|
"use constant INFO_BACKUP_KEY_CHECKSUM_PAGE => MANIFEST_KEY_CHECKSUM_PAGE;\n"
|
|
"push @EXPORT, qw(INFO_BACKUP_KEY_CHECKSUM_PAGE);\n"
|
|
"use constant INFO_BACKUP_KEY_DB_VERSION => MANIFEST_KEY_DB_VERSION;\n"
|
|
"push @EXPORT, qw(INFO_BACKUP_KEY_DB_VERSION);\n"
|
|
"use constant INFO_BACKUP_KEY_FORMAT => INI_KEY_FORMAT;\n"
|
|
"push @EXPORT, qw(INFO_BACKUP_KEY_FORMAT);\n"
|
|
"use constant INFO_BACKUP_KEY_HARDLINK => MANIFEST_KEY_HARDLINK;\n"
|
|
"push @EXPORT, qw(INFO_BACKUP_KEY_HARDLINK);\n"
|
|
"use constant INFO_BACKUP_KEY_HISTORY_ID => MANIFEST_KEY_DB_ID;\n"
|
|
"push @EXPORT, qw(INFO_BACKUP_KEY_HISTORY_ID);\n"
|
|
"use constant INFO_BACKUP_KEY_LABEL => MANIFEST_KEY_LABEL;\n"
|
|
"push @EXPORT, qw(INFO_BACKUP_KEY_LABEL);\n"
|
|
"use constant INFO_BACKUP_KEY_PRIOR => MANIFEST_KEY_PRIOR;\n"
|
|
"push @EXPORT, qw(INFO_BACKUP_KEY_PRIOR);\n"
|
|
"use constant INFO_BACKUP_KEY_REFERENCE => 'backup-reference';\n"
|
|
"push @EXPORT, qw(INFO_BACKUP_KEY_REFERENCE);\n"
|
|
"use constant INFO_BACKUP_KEY_ONLINE => MANIFEST_KEY_ONLINE;\n"
|
|
"push @EXPORT, qw(INFO_BACKUP_KEY_ONLINE);\n"
|
|
"use constant INFO_BACKUP_KEY_SYSTEM_ID => MANIFEST_KEY_SYSTEM_ID;\n"
|
|
"push @EXPORT, qw(INFO_BACKUP_KEY_SYSTEM_ID);\n"
|
|
"use constant INFO_BACKUP_KEY_TIMESTAMP_START => MANIFEST_KEY_TIMESTAMP_START;\n"
|
|
"push @EXPORT, qw(INFO_BACKUP_KEY_TIMESTAMP_START);\n"
|
|
"use constant INFO_BACKUP_KEY_TIMESTAMP_STOP => MANIFEST_KEY_TIMESTAMP_STOP;\n"
|
|
"push @EXPORT, qw(INFO_BACKUP_KEY_TIMESTAMP_STOP);\n"
|
|
"use constant INFO_BACKUP_KEY_TYPE => MANIFEST_KEY_TYPE;\n"
|
|
"push @EXPORT, qw(INFO_BACKUP_KEY_TYPE);\n"
|
|
"use constant INFO_BACKUP_KEY_VERSION => INI_KEY_VERSION;\n"
|
|
"push @EXPORT, qw(INFO_BACKUP_KEY_VERSION);\n"
|
|
"\n\n\n\n"
|
|
"my $strBackupInfoMissingMsg =\n"
|
|
"FILE_BACKUP_INFO . \" does not exist and is required to perform a backup.\\n\" .\n"
|
|
"\"HINT: has a stanza-create been performed?\";\n"
|
|
"\n\n\n\n"
|
|
"sub new\n"
|
|
"{\n"
|
|
"my $class = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$strBackupClusterPath,\n"
|
|
"$bValidate,\n"
|
|
"$bRequired,\n"
|
|
"$oStorage,\n"
|
|
"$bLoad,\n"
|
|
"$bIgnoreMissing, # Don't error on missing files\n"
|
|
"$strCipherPassSub,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->new', \\@_,\n"
|
|
"{name => 'strBackupClusterPath'},\n"
|
|
"{name => 'bValidate', default => true},\n"
|
|
"{name => 'bRequired', default => true},\n"
|
|
"{name => 'oStorage', optional => true, default => storageRepo()},\n"
|
|
"{name => 'bLoad', optional => true, default => true},\n"
|
|
"{name => 'bIgnoreMissing', optional => true, default => false},\n"
|
|
"{name => 'strCipherPassSub', optional => true},\n"
|
|
");\n"
|
|
"\n\n"
|
|
"my $strBackupInfoFile = \"${strBackupClusterPath}/\" . FILE_BACKUP_INFO;\n"
|
|
"my $self = {};\n"
|
|
"my $iResult = 0;\n"
|
|
"my $strResultMessage;\n"
|
|
"\n\n"
|
|
"eval\n"
|
|
"{\n"
|
|
"$self = $class->SUPER::new($strBackupInfoFile, {bLoad => $bLoad, bIgnoreMissing => $bIgnoreMissing,\n"
|
|
"oStorage => $oStorage, strCipherPass => $oStorage->cipherPassUser(),\n"
|
|
"strCipherPassSub => $strCipherPassSub});\n"
|
|
"return true;\n"
|
|
"}\n"
|
|
"or do\n"
|
|
"{\n"
|
|
"\n"
|
|
"$iResult = exceptionCode($EVAL_ERROR);\n"
|
|
"$strResultMessage = exceptionMessage($EVAL_ERROR);\n"
|
|
"};\n"
|
|
"\n"
|
|
"if ($iResult != 0)\n"
|
|
"{\n"
|
|
"\n\n"
|
|
"if ($iResult == ERROR_FILE_MISSING)\n"
|
|
"{\n"
|
|
"if ($bRequired)\n"
|
|
"{\n"
|
|
"confess &log(ERROR, \"${strBackupClusterPath}/$strBackupInfoMissingMsg\", ERROR_FILE_MISSING);\n"
|
|
"}\n"
|
|
"}\n"
|
|
"elsif ($iResult == ERROR_CRYPTO && $strResultMessage =~ \"^unable to flush\")\n"
|
|
"{\n"
|
|
"confess &log(ERROR, \"unable to parse '$strBackupInfoFile'\\nHINT: Is or was the repo encrypted?\", $iResult);\n"
|
|
"}\n"
|
|
"else\n"
|
|
"{\n"
|
|
"confess $EVAL_ERROR;\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n"
|
|
"$self->{strBackupClusterPath} = $strBackupClusterPath;\n"
|
|
"$self->{oStorage} = $oStorage;\n"
|
|
"\n\n"
|
|
"if ($bValidate)\n"
|
|
"{\n"
|
|
"$self->validate();\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'self', value => $self}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n\n\n"
|
|
"sub validate\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my ($strOperation) = logDebugParam(__PACKAGE__ . '->validate');\n"
|
|
"\n\n"
|
|
"$self->confirmExists();\n"
|
|
"\n"
|
|
"$self->reconstruct();\n"
|
|
"\n\n"
|
|
"return logDebugReturn($strOperation);\n"
|
|
"}\n"
|
|
"\n\n\n\n\n\n\n"
|
|
"sub reconstruct\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$bSave,\n"
|
|
"$bRequired,\n"
|
|
"$strDbVersion,\n"
|
|
"$ullDbSysId,\n"
|
|
"$iControlVersion,\n"
|
|
"$iCatalogVersion,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->reconstruct', \\@_,\n"
|
|
"{name => 'bSave', default => true},\n"
|
|
"{name => 'bRequired', default => true},\n"
|
|
"{name => 'strDbVersion', required => false},\n"
|
|
"{name => 'ullDbSysId', required => false},\n"
|
|
"{name => 'iControlVersion', required => false},\n"
|
|
"{name => 'iCatalogVersion', required => false},\n"
|
|
");\n"
|
|
"\n\n"
|
|
"foreach my $strBackup ($self->{oStorage}->list(\n"
|
|
"$self->{strBackupClusterPath}, {strExpression => backupRegExpGet(true, true, true)}))\n"
|
|
"{\n"
|
|
"my $strManifestFile = \"$self->{strBackupClusterPath}/${strBackup}/\" . FILE_MANIFEST;\n"
|
|
"\n\n\n\n"
|
|
"if (!$self->current($strBackup) && $self->{oStorage}->exists($strManifestFile))\n"
|
|
"{\n"
|
|
"my $oManifest = pgBackRest::Manifest->new($strManifestFile,\n"
|
|
"{strCipherPass => ($self->{oStorage}->encrypted($strManifestFile)) ? $self->cipherPassSub() : undef});\n"
|
|
"\n\n\n"
|
|
"if (!$bRequired)\n"
|
|
"{\n"
|
|
"my $hDbList = $self->dbHistoryList();\n"
|
|
"my $iDbId = $oManifest->get(MANIFEST_SECTION_BACKUP_DB, MANIFEST_KEY_DB_ID);\n"
|
|
"my $iDbIdMax = 0;\n"
|
|
"my $ullDbSysId = $oManifest->get(MANIFEST_SECTION_BACKUP_DB, MANIFEST_KEY_SYSTEM_ID);\n"
|
|
"my $strDbVersion = $oManifest->get(MANIFEST_SECTION_BACKUP_DB, MANIFEST_KEY_DB_VERSION);\n"
|
|
"\n\n"
|
|
"foreach my $iDbHistoryId (keys %{$hDbList})\n"
|
|
"{\n"
|
|
"\n"
|
|
"if ($iDbHistoryId > $iDbIdMax)\n"
|
|
"{\n"
|
|
"$iDbIdMax = $iDbHistoryId;\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n"
|
|
"if ($iDbId >= $iDbIdMax)\n"
|
|
"{\n"
|
|
"$self->dbSectionSet($strDbVersion, $oManifest->get(MANIFEST_SECTION_BACKUP_DB, MANIFEST_KEY_CONTROL),\n"
|
|
"$oManifest->get(MANIFEST_SECTION_BACKUP_DB, MANIFEST_KEY_CATALOG), $ullDbSysId, $iDbId);\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n"
|
|
"&log(WARN, \"backup ${strBackup} found in repository added to \" . FILE_BACKUP_INFO);\n"
|
|
"\n"
|
|
"$self->add($oManifest, $bSave, $bRequired);\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if (!$bRequired)\n"
|
|
"{\n"
|
|
"\n"
|
|
"if (!defined($strDbVersion) || !defined($ullDbSysId) || !defined($iControlVersion) || !defined($iCatalogVersion))\n"
|
|
"{\n"
|
|
"confess &log(ASSERT, \"backup info cannot be reconstructed without database information\");\n"
|
|
"}\n"
|
|
"\n"
|
|
"elsif (!$self->test(INFO_BACKUP_SECTION_DB))\n"
|
|
"{\n"
|
|
"$self->create($strDbVersion, $ullDbSysId, $iControlVersion, $iCatalogVersion, $bSave);\n"
|
|
"}\n"
|
|
"\n"
|
|
"else\n"
|
|
"{\n"
|
|
"\n"
|
|
"logDisable();\n"
|
|
"\n"
|
|
"eval\n"
|
|
"{\n"
|
|
"$self->check($strDbVersion, $iControlVersion, $iCatalogVersion, $ullDbSysId, $bRequired);\n"
|
|
"logEnable();\n"
|
|
"return true;\n"
|
|
"}\n"
|
|
"or do\n"
|
|
"{\n"
|
|
"\n"
|
|
"logEnable();\n"
|
|
"\n\n"
|
|
"confess $EVAL_ERROR if (exceptionCode($EVAL_ERROR) != ERROR_BACKUP_MISMATCH);\n"
|
|
"\n\n"
|
|
"$self->dbSectionSet($strDbVersion, $iControlVersion, $iCatalogVersion, $ullDbSysId, $self->dbHistoryIdGet(false)+1);\n"
|
|
"};\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"foreach my $strBackup ($self->keys(INFO_BACKUP_SECTION_BACKUP_CURRENT))\n"
|
|
"{\n"
|
|
"my $strManifestFile = \"$self->{strBackupClusterPath}/${strBackup}/\" . FILE_MANIFEST;\n"
|
|
"my $strBackupPath = \"$self->{strBackupClusterPath}/${strBackup}\";\n"
|
|
"\n"
|
|
"if (!$self->{oStorage}->pathExists($strBackupPath))\n"
|
|
"{\n"
|
|
"&log(WARN, \"backup ${strBackup} missing in repository removed from \" . FILE_BACKUP_INFO);\n"
|
|
"$self->delete($strBackup);\n"
|
|
"}\n"
|
|
"elsif (!$self->{oStorage}->exists($strManifestFile))\n"
|
|
"{\n"
|
|
"&log(WARN, \"backup ${strBackup} missing manifest removed from \" . FILE_BACKUP_INFO);\n"
|
|
"$self->delete($strBackup);\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"return logDebugReturn($strOperation);\n"
|
|
"}\n"
|
|
"\n\n\n\n\n\n"
|
|
"sub check\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$strDbVersion,\n"
|
|
"$iControlVersion,\n"
|
|
"$iCatalogVersion,\n"
|
|
"$ullDbSysId,\n"
|
|
"$bRequired,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->check', \\@_,\n"
|
|
"{name => 'strDbVersion', trace => true},\n"
|
|
"{name => 'iControlVersion', trace => true},\n"
|
|
"{name => 'iCatalogVersion', trace => true},\n"
|
|
"{name => 'ullDbSysId', trace => true},\n"
|
|
"{name => 'bRequired', default => true},\n"
|
|
");\n"
|
|
"\n\n"
|
|
"if ($bRequired)\n"
|
|
"{\n"
|
|
"$self->confirmExists();\n"
|
|
"}\n"
|
|
"\n"
|
|
"if (!$self->test(INFO_BACKUP_SECTION_DB, INFO_BACKUP_KEY_SYSTEM_ID, undef, $ullDbSysId) ||\n"
|
|
"!$self->test(INFO_BACKUP_SECTION_DB, INFO_BACKUP_KEY_DB_VERSION, undef, $strDbVersion))\n"
|
|
"{\n"
|
|
"confess &log(ERROR, \"database version = ${strDbVersion}, system-id ${ullDbSysId} does not match backup version = \" .\n"
|
|
"$self->get(INFO_BACKUP_SECTION_DB, INFO_BACKUP_KEY_DB_VERSION) . \", system-id = \" .\n"
|
|
"$self->get(INFO_BACKUP_SECTION_DB, INFO_BACKUP_KEY_SYSTEM_ID) . \"\\n\" .\n"
|
|
"\"HINT: is this the correct stanza?\", ERROR_BACKUP_MISMATCH);\n"
|
|
"}\n"
|
|
"\n"
|
|
"if (!$self->test(INFO_BACKUP_SECTION_DB, INFO_BACKUP_KEY_CATALOG, undef, $iCatalogVersion) ||\n"
|
|
"!$self->test(INFO_BACKUP_SECTION_DB, INFO_BACKUP_KEY_CONTROL, undef, $iControlVersion))\n"
|
|
"{\n"
|
|
"confess &log(ERROR, \"database control-version = ${iControlVersion}, catalog-version ${iCatalogVersion}\" .\n"
|
|
"\" does not match backup control-version = \" .\n"
|
|
"$self->get(INFO_BACKUP_SECTION_DB, INFO_BACKUP_KEY_CONTROL) . \", catalog-version = \" .\n"
|
|
"$self->get(INFO_BACKUP_SECTION_DB, INFO_BACKUP_KEY_CATALOG) . \"\\n\" .\n"
|
|
"\"HINT: this may be a symptom of database or repository corruption!\", ERROR_BACKUP_MISMATCH);\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'iDbHistoryId', value => $self->numericGet(INFO_BACKUP_SECTION_DB, INFO_BACKUP_KEY_HISTORY_ID)}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n\n\n"
|
|
"sub add\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$oBackupManifest,\n"
|
|
"$bSave,\n"
|
|
"$bRequired,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->add', \\@_,\n"
|
|
"{name => 'oBackupManifest', trace => true},\n"
|
|
"{name => 'bSave', default => true, trace => true},\n"
|
|
"{name => 'bRequired', default => true, trace => true},\n"
|
|
");\n"
|
|
"\n\n"
|
|
"if ($bRequired)\n"
|
|
"{\n"
|
|
"$self->confirmExists();\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"my $strBackupLabel = $oBackupManifest->get(MANIFEST_SECTION_BACKUP, MANIFEST_KEY_LABEL);\n"
|
|
"\n\n"
|
|
"my $lBackupSize = 0;\n"
|
|
"my $lBackupSizeDelta = 0;\n"
|
|
"my $lBackupRepoSize = 0;\n"
|
|
"my $lBackupRepoSizeDelta = 0;\n"
|
|
"my $oReferenceHash = undef;\n"
|
|
"\n"
|
|
"foreach my $strFileKey ($oBackupManifest->keys(MANIFEST_SECTION_TARGET_FILE))\n"
|
|
"{\n"
|
|
"my $lFileSize =\n"
|
|
"$oBackupManifest->get(MANIFEST_SECTION_TARGET_FILE, $strFileKey, MANIFEST_SUBKEY_SIZE);\n"
|
|
"my $lRepoSize =\n"
|
|
"$oBackupManifest->get(MANIFEST_SECTION_TARGET_FILE, $strFileKey, MANIFEST_SUBKEY_REPO_SIZE, false, $lFileSize);\n"
|
|
"my $strFileReference =\n"
|
|
"$oBackupManifest->get(MANIFEST_SECTION_TARGET_FILE, $strFileKey, MANIFEST_SUBKEY_REFERENCE, false);\n"
|
|
"\n\n"
|
|
"$lBackupSize += $lFileSize;\n"
|
|
"$lBackupRepoSize += $lRepoSize;\n"
|
|
"\n"
|
|
"if (defined($strFileReference))\n"
|
|
"{\n"
|
|
"$$oReferenceHash{$strFileReference} = true;\n"
|
|
"}\n"
|
|
"else\n"
|
|
"{\n"
|
|
"$lBackupSizeDelta += $lFileSize;\n"
|
|
"$lBackupRepoSizeDelta += $lRepoSize;\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"$self->numericSet(INFO_BACKUP_SECTION_BACKUP_CURRENT, $strBackupLabel, INFO_BACKUP_KEY_BACKUP_SIZE, $lBackupSize);\n"
|
|
"$self->numericSet(INFO_BACKUP_SECTION_BACKUP_CURRENT, $strBackupLabel, INFO_BACKUP_KEY_BACKUP_SIZE_DELTA, $lBackupSizeDelta);\n"
|
|
"$self->numericSet(INFO_BACKUP_SECTION_BACKUP_CURRENT, $strBackupLabel, INFO_BACKUP_KEY_BACKUP_REPO_SIZE, $lBackupRepoSize);\n"
|
|
"$self->numericSet(INFO_BACKUP_SECTION_BACKUP_CURRENT, $strBackupLabel, INFO_BACKUP_KEY_BACKUP_REPO_SIZE_DELTA,\n"
|
|
"$lBackupRepoSizeDelta);\n"
|
|
"\n"
|
|
"$self->boolSet(INFO_BACKUP_SECTION_BACKUP_CURRENT, $strBackupLabel, INFO_BACKUP_KEY_ARCHIVE_CHECK,\n"
|
|
"$oBackupManifest->boolGet(MANIFEST_SECTION_BACKUP_OPTION, MANIFEST_KEY_ARCHIVE_CHECK));\n"
|
|
"$self->boolSet(INFO_BACKUP_SECTION_BACKUP_CURRENT, $strBackupLabel, INFO_BACKUP_KEY_ARCHIVE_COPY,\n"
|
|
"$oBackupManifest->boolGet(MANIFEST_SECTION_BACKUP_OPTION, MANIFEST_KEY_ARCHIVE_COPY));\n"
|
|
"$self->set(INFO_BACKUP_SECTION_BACKUP_CURRENT, $strBackupLabel, INFO_BACKUP_KEY_ARCHIVE_START,\n"
|
|
"$oBackupManifest->get(MANIFEST_SECTION_BACKUP, MANIFEST_KEY_ARCHIVE_START, undef, false));\n"
|
|
"$self->set(INFO_BACKUP_SECTION_BACKUP_CURRENT, $strBackupLabel, INFO_BACKUP_KEY_ARCHIVE_STOP,\n"
|
|
"$oBackupManifest->get(MANIFEST_SECTION_BACKUP, MANIFEST_KEY_ARCHIVE_STOP, undef, false));\n"
|
|
"$self->boolSet(INFO_BACKUP_SECTION_BACKUP_CURRENT, $strBackupLabel, INFO_BACKUP_KEY_BACKUP_STANDBY,\n"
|
|
"$oBackupManifest->boolGet(MANIFEST_SECTION_BACKUP_OPTION, MANIFEST_KEY_BACKUP_STANDBY));\n"
|
|
"$self->boolSet(INFO_BACKUP_SECTION_BACKUP_CURRENT, $strBackupLabel, INFO_BACKUP_KEY_CHECKSUM_PAGE,\n"
|
|
"$oBackupManifest->boolGet(MANIFEST_SECTION_BACKUP_OPTION, MANIFEST_KEY_CHECKSUM_PAGE));\n"
|
|
"$self->boolSet(INFO_BACKUP_SECTION_BACKUP_CURRENT, $strBackupLabel, INFO_BACKUP_KEY_COMPRESS,\n"
|
|
"$oBackupManifest->boolGet(MANIFEST_SECTION_BACKUP_OPTION, MANIFEST_KEY_COMPRESS));\n"
|
|
"$self->numericSet(INFO_BACKUP_SECTION_BACKUP_CURRENT, $strBackupLabel, INFO_BACKUP_KEY_FORMAT,\n"
|
|
"$oBackupManifest->numericGet(INI_SECTION_BACKREST, INI_KEY_FORMAT));\n"
|
|
"$self->boolSet(INFO_BACKUP_SECTION_BACKUP_CURRENT, $strBackupLabel, INFO_BACKUP_KEY_HARDLINK,\n"
|
|
"$oBackupManifest->boolGet(MANIFEST_SECTION_BACKUP_OPTION, MANIFEST_KEY_HARDLINK));\n"
|
|
"$self->boolSet(INFO_BACKUP_SECTION_BACKUP_CURRENT, $strBackupLabel, INFO_BACKUP_KEY_ONLINE,\n"
|
|
"$oBackupManifest->boolGet(MANIFEST_SECTION_BACKUP_OPTION, MANIFEST_KEY_ONLINE));\n"
|
|
"$self->numericSet(INFO_BACKUP_SECTION_BACKUP_CURRENT, $strBackupLabel, INFO_BACKUP_KEY_TIMESTAMP_START,\n"
|
|
"$oBackupManifest->numericGet(MANIFEST_SECTION_BACKUP, MANIFEST_KEY_TIMESTAMP_START));\n"
|
|
"$self->numericSet(INFO_BACKUP_SECTION_BACKUP_CURRENT, $strBackupLabel, INFO_BACKUP_KEY_TIMESTAMP_STOP,\n"
|
|
"$oBackupManifest->numericGet(MANIFEST_SECTION_BACKUP, MANIFEST_KEY_TIMESTAMP_STOP));\n"
|
|
"$self->set(INFO_BACKUP_SECTION_BACKUP_CURRENT, $strBackupLabel, INFO_BACKUP_KEY_TYPE,\n"
|
|
"$oBackupManifest->get(MANIFEST_SECTION_BACKUP, MANIFEST_KEY_TYPE));\n"
|
|
"$self->set(INFO_BACKUP_SECTION_BACKUP_CURRENT, $strBackupLabel, INFO_BACKUP_KEY_VERSION,\n"
|
|
"$oBackupManifest->get(INI_SECTION_BACKREST, INI_KEY_VERSION));\n"
|
|
"\n"
|
|
"if ($bRequired)\n"
|
|
"{\n"
|
|
"$self->set(INFO_BACKUP_SECTION_BACKUP_CURRENT, $strBackupLabel, INFO_BACKUP_KEY_HISTORY_ID,\n"
|
|
"$self->get(INFO_BACKUP_SECTION_DB, INFO_BACKUP_KEY_HISTORY_ID));\n"
|
|
"}\n"
|
|
"\n"
|
|
"else\n"
|
|
"{\n"
|
|
"$self->set(INFO_BACKUP_SECTION_BACKUP_CURRENT, $strBackupLabel, INFO_BACKUP_KEY_HISTORY_ID,\n"
|
|
"$oBackupManifest->get(MANIFEST_SECTION_BACKUP_DB, MANIFEST_KEY_DB_ID));\n"
|
|
"}\n"
|
|
"\n"
|
|
"if (!$oBackupManifest->test(MANIFEST_SECTION_BACKUP, MANIFEST_KEY_TYPE, undef, CFGOPTVAL_BACKUP_TYPE_FULL))\n"
|
|
"{\n"
|
|
"my @stryReference = sort(keys(%$oReferenceHash));\n"
|
|
"\n"
|
|
"$self->set(INFO_BACKUP_SECTION_BACKUP_CURRENT, $strBackupLabel, INFO_BACKUP_KEY_PRIOR,\n"
|
|
"$oBackupManifest->get(MANIFEST_SECTION_BACKUP, MANIFEST_KEY_PRIOR));\n"
|
|
"$self->set(INFO_BACKUP_SECTION_BACKUP_CURRENT, $strBackupLabel, INFO_BACKUP_KEY_REFERENCE,\n"
|
|
"\\@stryReference);\n"
|
|
"}\n"
|
|
"\n"
|
|
"if ($bSave)\n"
|
|
"{\n"
|
|
"$self->save();\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"return logDebugReturn($strOperation);\n"
|
|
"}\n"
|
|
"\n\n\n\n\n\n"
|
|
"sub current\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$strBackup\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->current', \\@_,\n"
|
|
"{name => 'strBackup'}\n"
|
|
");\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'bTest', value => $self->test(INFO_BACKUP_SECTION_BACKUP_CURRENT, $strBackup)}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n\n\n"
|
|
"sub list\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$strFilter,\n"
|
|
"$strOrder\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->list', \\@_,\n"
|
|
"{name => 'strFilter', required => false},\n"
|
|
"{name => 'strOrder', default => 'forward'}\n"
|
|
");\n"
|
|
"\n\n"
|
|
"my @stryBackup;\n"
|
|
"\n\n"
|
|
"for my $strBackup ($self->keys(INFO_BACKUP_SECTION_BACKUP_CURRENT))\n"
|
|
"{\n"
|
|
"if (!defined($strFilter) || $strBackup =~ $strFilter)\n"
|
|
"{\n"
|
|
"if ($strOrder eq 'reverse')\n"
|
|
"{\n"
|
|
"unshift(@stryBackup, $strBackup)\n"
|
|
"}\n"
|
|
"else\n"
|
|
"{\n"
|
|
"push(@stryBackup, $strBackup)\n"
|
|
"}\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'stryBackup', value => \\@stryBackup}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n\n\n"
|
|
"sub backupArchiveDbHistoryId\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$strArchiveId,\n"
|
|
"$strPathBackupArchive,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->backupArchiveDbHistoryId', \\@_,\n"
|
|
"{name => 'strArchiveId'},\n"
|
|
"{name => 'strPathBackupArchive'},\n"
|
|
");\n"
|
|
"\n\n"
|
|
"my @stryArchiveBackup;\n"
|
|
"\n\n"
|
|
"my $oArchiveInfo = new pgBackRest::Archive::Info($strPathBackupArchive, true);\n"
|
|
"my $hDbListArchive = $oArchiveInfo->dbHistoryList();\n"
|
|
"my $hDbListBackup = $self->dbHistoryList();\n"
|
|
"my $iDbHistoryId = undef;\n"
|
|
"\n\n"
|
|
"my ($strDbVersionArchive, $iDbIdArchive) = split(\"-\", $strArchiveId);\n"
|
|
"\n\n"
|
|
"if (exists($hDbListArchive->{$iDbIdArchive}))\n"
|
|
"{\n"
|
|
"my $ullDbSysIdArchive = $$hDbListArchive{$iDbIdArchive}{&INFO_SYSTEM_ID};\n"
|
|
"\n\n\n"
|
|
"foreach my $iDbIdBackup (sort {$b <=> $a} keys %{$hDbListBackup})\n"
|
|
"{\n"
|
|
"if ($$hDbListBackup{$iDbIdBackup}{&INFO_SYSTEM_ID} == $ullDbSysIdArchive &&\n"
|
|
"$$hDbListBackup{$iDbIdBackup}{&INFO_DB_VERSION} eq $strDbVersionArchive)\n"
|
|
"{\n"
|
|
"$iDbHistoryId = $iDbIdBackup;\n"
|
|
"last;\n"
|
|
"}\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if (!defined($iDbHistoryId))\n"
|
|
"{\n"
|
|
"\n"
|
|
"if (!($oArchiveInfo->test(INFO_ARCHIVE_SECTION_DB, INFO_ARCHIVE_KEY_DB_VERSION, undef,\n"
|
|
"($self->get(INFO_BACKUP_SECTION_DB, INFO_BACKUP_KEY_DB_VERSION)))) ||\n"
|
|
"!($oArchiveInfo->test(INFO_ARCHIVE_SECTION_DB, INFO_ARCHIVE_KEY_DB_SYSTEM_ID, undef,\n"
|
|
"($self->get(INFO_BACKUP_SECTION_DB, INFO_BACKUP_KEY_SYSTEM_ID)))))\n"
|
|
"{\n"
|
|
"\n"
|
|
"confess &log(ASSERT, \"the archive and backup database sections do not match\", ERROR_FILE_INVALID);\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'iDbHistoryId', value => $iDbHistoryId}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n\n\n"
|
|
"sub listByArchiveId\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$strArchiveId,\n"
|
|
"$strPathBackupArchive,\n"
|
|
"$stryBackup,\n"
|
|
"$strOrder,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->listByArchiveId', \\@_,\n"
|
|
"{name => 'strArchiveId'},\n"
|
|
"{name => 'strPathBackupArchive'},\n"
|
|
"{name => 'stryBackup'},\n"
|
|
"{name => 'strOrder', default => 'forward'}\n"
|
|
");\n"
|
|
"\n\n"
|
|
"my @stryArchiveBackup;\n"
|
|
"\n"
|
|
"my $iDbHistoryId = $self->backupArchiveDbHistoryId($strArchiveId, $strPathBackupArchive);\n"
|
|
"\n\n"
|
|
"if (defined($iDbHistoryId))\n"
|
|
"{\n"
|
|
"\n"
|
|
"foreach my $strBackup (@$stryBackup)\n"
|
|
"{\n"
|
|
"\n"
|
|
"if ($self->test(INFO_BACKUP_SECTION_BACKUP_CURRENT, $strBackup, INFO_BACKUP_KEY_HISTORY_ID, $iDbHistoryId))\n"
|
|
"{\n"
|
|
"if ($strOrder eq 'reverse')\n"
|
|
"{\n"
|
|
"unshift(@stryArchiveBackup, $strBackup)\n"
|
|
"}\n"
|
|
"else\n"
|
|
"{\n"
|
|
"push(@stryArchiveBackup, $strBackup)\n"
|
|
"}\n"
|
|
"}\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'stryArchiveBackup', value => \\@stryArchiveBackup}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n\n\n"
|
|
"sub last\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$strType\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->last', \\@_,\n"
|
|
"{name => 'strType'}\n"
|
|
");\n"
|
|
"\n"
|
|
"my $strFilter = backupRegExpGet(true, $strType ne CFGOPTVAL_BACKUP_TYPE_FULL, $strType eq CFGOPTVAL_BACKUP_TYPE_INCR);\n"
|
|
"my $strBackup = ($self->list($strFilter, 'reverse'))[0];\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'strBackup', value => $strBackup}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n\n\n"
|
|
"sub delete\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$strBackupLabel\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->delete', \\@_,\n"
|
|
"{name => 'strBackupLabel'}\n"
|
|
");\n"
|
|
"\n"
|
|
"$self->remove(INFO_BACKUP_SECTION_BACKUP_CURRENT, $strBackupLabel);\n"
|
|
"\n\n"
|
|
"return logDebugReturn($strOperation);\n"
|
|
"}\n"
|
|
"\n\n\n\n\n\n"
|
|
"sub create\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$strDbVersion,\n"
|
|
"$ullDbSysId,\n"
|
|
"$iControlVersion,\n"
|
|
"$iCatalogVersion,\n"
|
|
"$bSave,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->create', \\@_,\n"
|
|
"{name => 'strDbVersion'},\n"
|
|
"{name => 'ullDbSysId'},\n"
|
|
"{name => 'iControlVersion'},\n"
|
|
"{name => 'iCatalogVersion'},\n"
|
|
"{name => 'bSave', default => true},\n"
|
|
");\n"
|
|
"\n\n"
|
|
"$self->dbSectionSet($strDbVersion, $iControlVersion, $iCatalogVersion, $ullDbSysId, $self->dbHistoryIdGet(false));\n"
|
|
"\n"
|
|
"if ($bSave)\n"
|
|
"{\n"
|
|
"$self->save();\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"return logDebugReturn($strOperation);\n"
|
|
"}\n"
|
|
"\n\n\n\n\n\n"
|
|
"sub dbHistoryIdGet\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$bFileRequired,\n"
|
|
") = logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->dbHistoryIdGet', \\@_,\n"
|
|
"{name => 'bFileRequired', default => true},\n"
|
|
");\n"
|
|
"\n\n"
|
|
"if ($bFileRequired)\n"
|
|
"{\n"
|
|
"$self->confirmExists();\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"my $iDbHistoryId = (!$self->test(INFO_BACKUP_SECTION_DB))\n"
|
|
"? 1 : $self->numericGet(INFO_BACKUP_SECTION_DB, INFO_BACKUP_KEY_HISTORY_ID);\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'iDbHistoryId', value => $iDbHistoryId}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n\n\n"
|
|
"sub dbHistoryList\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
") = logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->dbHistoryList',\n"
|
|
");\n"
|
|
"\n"
|
|
"my %hDbHash;\n"
|
|
"\n"
|
|
"foreach my $iHistoryId ($self->keys(INFO_BACKUP_SECTION_DB_HISTORY))\n"
|
|
"{\n"
|
|
"$hDbHash{$iHistoryId}{&INFO_DB_VERSION} =\n"
|
|
"$self->get(INFO_BACKUP_SECTION_DB_HISTORY, $iHistoryId, INFO_BACKUP_KEY_DB_VERSION);\n"
|
|
"$hDbHash{$iHistoryId}{&INFO_SYSTEM_ID} =\n"
|
|
"$self->get(INFO_BACKUP_SECTION_DB_HISTORY, $iHistoryId, INFO_BACKUP_KEY_SYSTEM_ID);\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'hDbHash', value => \\%hDbHash}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n\n\n"
|
|
"sub dbSectionSet\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$strDbVersion,\n"
|
|
"$iControlVersion,\n"
|
|
"$iCatalogVersion,\n"
|
|
"$ullDbSysId,\n"
|
|
"$iDbHistoryId,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->dbSectionSet', \\@_,\n"
|
|
"{name => 'strDbVersion', trace => true},\n"
|
|
"{name => 'iControlVersion', trace => true},\n"
|
|
"{name => 'iCatalogVersion', trace => true},\n"
|
|
"{name => 'ullDbSysId', trace => true},\n"
|
|
"{name => 'iDbHistoryId', trace => true},\n"
|
|
");\n"
|
|
"\n\n"
|
|
"$self->numericSet(INFO_BACKUP_SECTION_DB, INFO_BACKUP_KEY_CATALOG, undef, $iCatalogVersion);\n"
|
|
"$self->numericSet(INFO_BACKUP_SECTION_DB, INFO_BACKUP_KEY_CONTROL, undef, $iControlVersion);\n"
|
|
"$self->numericSet(INFO_BACKUP_SECTION_DB, INFO_BACKUP_KEY_SYSTEM_ID, undef, $ullDbSysId);\n"
|
|
"$self->set(INFO_BACKUP_SECTION_DB, INFO_BACKUP_KEY_DB_VERSION, undef, $strDbVersion);\n"
|
|
"$self->numericSet(INFO_BACKUP_SECTION_DB, INFO_BACKUP_KEY_HISTORY_ID, undef, $iDbHistoryId);\n"
|
|
"\n\n"
|
|
"$self->numericSet(INFO_BACKUP_SECTION_DB_HISTORY, $iDbHistoryId, INFO_BACKUP_KEY_CATALOG, $iCatalogVersion);\n"
|
|
"$self->numericSet(INFO_BACKUP_SECTION_DB_HISTORY, $iDbHistoryId, INFO_BACKUP_KEY_CONTROL, $iControlVersion);\n"
|
|
"$self->numericSet(INFO_BACKUP_SECTION_DB_HISTORY, $iDbHistoryId, INFO_BACKUP_KEY_SYSTEM_ID, $ullDbSysId);\n"
|
|
"$self->set(INFO_BACKUP_SECTION_DB_HISTORY, $iDbHistoryId, INFO_BACKUP_KEY_DB_VERSION, $strDbVersion);\n"
|
|
"\n\n"
|
|
"return logDebugReturn($strOperation);\n"
|
|
"}\n"
|
|
"\n\n\n\n\n\n\n"
|
|
"sub confirmDb\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$strBackup,\n"
|
|
"$strDbVersion,\n"
|
|
"$ullDbSysId,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->confirmDb', \\@_,\n"
|
|
"{name => 'strBackup', trace => true},\n"
|
|
"{name => 'strDbVersion', trace => true},\n"
|
|
"{name => 'ullDbSysId', trace => true},\n"
|
|
");\n"
|
|
"\n"
|
|
"my $bConfirmDb = undef;\n"
|
|
"\n\n"
|
|
"my $iDbHistoryId = $self->get(INFO_BACKUP_SECTION_BACKUP_CURRENT, $strBackup, INFO_BACKUP_KEY_HISTORY_ID);\n"
|
|
"\n\n"
|
|
"my $hDbList = $self->dbHistoryList();\n"
|
|
"\n\n"
|
|
"if (exists $hDbList->{$iDbHistoryId})\n"
|
|
"{\n"
|
|
"\n"
|
|
"if (($hDbList->{$iDbHistoryId}{&INFO_DB_VERSION} eq $strDbVersion) &&\n"
|
|
"($hDbList->{$iDbHistoryId}{&INFO_SYSTEM_ID} eq $ullDbSysId))\n"
|
|
"{\n"
|
|
"$bConfirmDb = true;\n"
|
|
"}\n"
|
|
"else\n"
|
|
"{\n"
|
|
"$bConfirmDb = false;\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n"
|
|
"else\n"
|
|
"{\n"
|
|
"confess &log(ERROR, \"backup info file is missing database history information for an existing backup\", ERROR_FILE_INVALID);\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'bConfirmDb', value => $bConfirmDb}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n\n\n"
|
|
"sub confirmExists\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"if (!$self->test(INFO_BACKUP_SECTION_DB) || !$self->{bExists})\n"
|
|
"{\n"
|
|
"confess &log(ERROR, $self->{strBackupClusterPath} . \"/\" . $strBackupInfoMissingMsg, ERROR_FILE_MISSING);\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n"
|
|
"1;\n"
|
|
},
|
|
{
|
|
.name = "pgBackRest/Check/Check.pm",
|
|
.data =
|
|
"\n\n\n"
|
|
"package pgBackRest::Check::Check;\n"
|
|
"\n"
|
|
"use strict;\n"
|
|
"use warnings FATAL => qw(all);\n"
|
|
"use Carp qw(confess);\n"
|
|
"use English '-no_match_vars';\n"
|
|
"\n"
|
|
"use pgBackRest::Archive::Common;\n"
|
|
"use pgBackRest::Archive::Get::File;\n"
|
|
"use pgBackRest::Backup::Info;\n"
|
|
"use pgBackRest::Common::Exception;\n"
|
|
"use pgBackRest::Common::Log;\n"
|
|
"use pgBackRest::Common::Wait;\n"
|
|
"use pgBackRest::Config::Config;\n"
|
|
"use pgBackRest::Db;\n"
|
|
"use pgBackRest::Manifest;\n"
|
|
"use pgBackRest::Protocol::Helper;\n"
|
|
"use pgBackRest::Protocol::Storage::Helper;\n"
|
|
"\n\n\n\n"
|
|
"sub new\n"
|
|
"{\n"
|
|
"my $class = shift;\n"
|
|
"\n\n"
|
|
"my ($strOperation) = logDebugParam(__PACKAGE__ . '->new');\n"
|
|
"\n\n"
|
|
"my $self = {};\n"
|
|
"bless $self, $class;\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'self', value => $self}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n\n\n\n\n"
|
|
"sub process\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my $strOperation = logDebugParam(__PACKAGE__ . '->process');\n"
|
|
"\n\n"
|
|
"my $iArchiveTimeout = cfgOption(CFGOPT_ARCHIVE_TIMEOUT);\n"
|
|
"\n"
|
|
"my $iResult = 0;\n"
|
|
"my $strResultMessage = undef;\n"
|
|
"\n"
|
|
"my $strArchiveId = undef;\n"
|
|
"my $strArchiveFile = undef;\n"
|
|
"my $strWalSegment = undef;\n"
|
|
"\n\n"
|
|
"my ($oDb) = dbMasterGet();\n"
|
|
"\n\n"
|
|
"my ($strDbVersion, $iControlVersion, $iCatalogVersion, $ullDbSysId) = $oDb->info();\n"
|
|
"\n\n"
|
|
"logLevelSet(undef, OFF);\n"
|
|
"\n\n"
|
|
"for (my $iRemoteIdx = 1; $iRemoteIdx <= cfgOptionIndexTotal(CFGOPT_PG_HOST); $iRemoteIdx++)\n"
|
|
"{\n"
|
|
"\n"
|
|
"if (cfgOptionTest(cfgOptionIdFromIndex(CFGOPT_PG_PATH, $iRemoteIdx)) ||\n"
|
|
"cfgOptionTest(cfgOptionIdFromIndex(CFGOPT_PG_HOST, $iRemoteIdx)))\n"
|
|
"{\n"
|
|
"eval\n"
|
|
"{\n"
|
|
"\n\n"
|
|
"my $oBackupManifest = new pgBackRest::Manifest(\"/dev/null/manifest.chk\",\n"
|
|
"{bLoad => false, strDbVersion => $strDbVersion, iDbCatalogVersion => $iCatalogVersion,\n"
|
|
"strCipherPass => 'x', strCipherPassSub => 'x'});\n"
|
|
"\n\n"
|
|
"$oBackupManifest->numericSet(MANIFEST_SECTION_BACKUP_DB, MANIFEST_KEY_DB_ID, undef, 1);\n"
|
|
"$oBackupManifest->numericSet(MANIFEST_SECTION_BACKUP_DB, MANIFEST_KEY_CONTROL, undef, $iControlVersion);\n"
|
|
"$oBackupManifest->numericSet(MANIFEST_SECTION_BACKUP_DB, MANIFEST_KEY_SYSTEM_ID, undef, $ullDbSysId);\n"
|
|
"\n"
|
|
"$oBackupManifest->build(\n"
|
|
"storageDb({iRemoteIdx => $iRemoteIdx}), cfgOption(cfgOptionIdFromIndex(CFGOPT_PG_PATH, $iRemoteIdx)), undef,\n"
|
|
"cfgOptionValid(CFGOPT_ONLINE) && cfgOption(CFGOPT_ONLINE), false, $oDb->tablespaceMapGet());\n"
|
|
"\n"
|
|
"return true;\n"
|
|
"}\n"
|
|
"or do\n"
|
|
"{\n"
|
|
"\n"
|
|
"$strResultMessage = \"Database: ${strDbVersion} ${ullDbSysId} \" . exceptionMessage($EVAL_ERROR) .\n"
|
|
"(($iResult != 0) ? \"\\n[$iResult] : $strResultMessage\" : \"\");\n"
|
|
"$iResult = exceptionCode($EVAL_ERROR);\n"
|
|
"};\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"logLevelSet(undef, cfgOption(CFGOPT_LOG_LEVEL_CONSOLE));\n"
|
|
"\n\n"
|
|
"if ($iResult == 0)\n"
|
|
"{\n"
|
|
"\n\n"
|
|
"($oDb) = dbObjectGet();\n"
|
|
"\n\n"
|
|
"$oDb->configValidate();\n"
|
|
"\n\n"
|
|
"logLevelSet(undef, OFF);\n"
|
|
"\n\n"
|
|
"eval\n"
|
|
"{\n"
|
|
"\n"
|
|
"$self->backupInfoCheck();\n"
|
|
"return true;\n"
|
|
"}\n"
|
|
"\n"
|
|
"or do\n"
|
|
"{\n"
|
|
"\n"
|
|
"$iResult = exceptionCode($EVAL_ERROR);\n"
|
|
"$strResultMessage = exceptionMessage($EVAL_ERROR);\n"
|
|
"};\n"
|
|
"\n\n"
|
|
"if ($iResult == 0)\n"
|
|
"{\n"
|
|
"eval\n"
|
|
"{\n"
|
|
"\n"
|
|
"($strArchiveId) = archiveGetCheck();\n"
|
|
"return true;\n"
|
|
"}\n"
|
|
"or do\n"
|
|
"{\n"
|
|
"\n"
|
|
"$iResult = exceptionCode($EVAL_ERROR);\n"
|
|
"$strResultMessage = exceptionMessage($EVAL_ERROR);\n"
|
|
"};\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if ($iResult == 0 && !$oDb->isStandby())\n"
|
|
"{\n"
|
|
"$strWalSegment = $oDb->walSwitch();\n"
|
|
"\n"
|
|
"eval\n"
|
|
"{\n"
|
|
"$strArchiveFile = walSegmentFind(storageRepo(), $strArchiveId, $strWalSegment, $iArchiveTimeout);\n"
|
|
"return true;\n"
|
|
"}\n"
|
|
"\n"
|
|
"or do\n"
|
|
"{\n"
|
|
"\n"
|
|
"$iResult = exceptionCode($EVAL_ERROR);\n"
|
|
"$strResultMessage = exceptionMessage($EVAL_ERROR);\n"
|
|
"};\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"logLevelSet(undef, cfgOption(CFGOPT_LOG_LEVEL_CONSOLE));\n"
|
|
"}\n"
|
|
"\n\n\n"
|
|
"if ($iResult == 0)\n"
|
|
"{\n"
|
|
"if (!$oDb->isStandby())\n"
|
|
"{\n"
|
|
"&log(INFO,\n"
|
|
"\"WAL segment ${strWalSegment} successfully stored in the archive at '\" .\n"
|
|
"storageRepo()->pathGet(STORAGE_REPO_ARCHIVE . \"/$strArchiveId/${strArchiveFile}\") . \"'\");\n"
|
|
"}\n"
|
|
"else\n"
|
|
"{\n"
|
|
"&log(INFO, 'switch ' . $oDb->walId() . ' cannot be performed on the standby, all other checks passed successfully');\n"
|
|
"}\n"
|
|
"}\n"
|
|
"else\n"
|
|
"{\n"
|
|
"\n"
|
|
"&log(ERROR, $strResultMessage, $iResult);\n"
|
|
"\n\n"
|
|
"if (defined($strWalSegment) && !defined($strArchiveFile))\n"
|
|
"{\n"
|
|
"&log(WARN,\n"
|
|
"\"WAL segment ${strWalSegment} did not reach the archive:\" . (defined($strArchiveId) ? $strArchiveId : '') . \"\\n\" .\n"
|
|
"\"HINT: Check the archive_command to ensure that all options are correct (especially --stanza).\\n\" .\n"
|
|
"\"HINT: Check the PostgreSQL server log for errors.\");\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'iResult', value => $iResult, trace => true}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n\n\n"
|
|
"sub backupInfoCheck\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$strDbVersion,\n"
|
|
"$iControlVersion,\n"
|
|
"$iCatalogVersion,\n"
|
|
"$ullDbSysId,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->backupInfoCheck', \\@_,\n"
|
|
"{name => 'strDbVersion', required => false},\n"
|
|
"{name => 'iControlVersion', required => false},\n"
|
|
"{name => 'iCatalogVersion', required => false},\n"
|
|
"{name => 'ullDbSysId', required => false}\n"
|
|
");\n"
|
|
"\n\n"
|
|
"my $iDbHistoryId;\n"
|
|
"\n"
|
|
"if (!defined($strDbVersion) || !defined($iControlVersion) || !defined($iCatalogVersion) || !defined($ullDbSysId))\n"
|
|
"{\n"
|
|
"\n"
|
|
"($strDbVersion, $iControlVersion, $iCatalogVersion, $ullDbSysId) = dbMasterGet()->info();\n"
|
|
"}\n"
|
|
"\n"
|
|
"if (!isRepoLocal())\n"
|
|
"{\n"
|
|
"$iDbHistoryId = protocolGet(CFGOPTVAL_REMOTE_TYPE_BACKUP)->cmdExecute(\n"
|
|
"OP_CHECK_BACKUP_INFO_CHECK, [$strDbVersion, $iControlVersion, $iCatalogVersion, $ullDbSysId]);\n"
|
|
"}\n"
|
|
"else\n"
|
|
"{\n"
|
|
"$iDbHistoryId = (new pgBackRest::Backup::Info(storageRepo()->pathGet(STORAGE_REPO_BACKUP)))->check(\n"
|
|
"$strDbVersion, $iControlVersion, $iCatalogVersion, $ullDbSysId);\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'iDbHistoryId', value => $iDbHistoryId, trace => true}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n"
|
|
"1;\n"
|
|
},
|
|
{
|
|
.name = "pgBackRest/Common/Cipher.pm",
|
|
.data =
|
|
"\n\n\n"
|
|
"package pgBackRest::Common::Cipher;\n"
|
|
"\n"
|
|
"use strict;\n"
|
|
"use warnings FATAL => qw(all);\n"
|
|
"use Carp qw(confess);\n"
|
|
"use English '-no_match_vars';\n"
|
|
"\n"
|
|
"use Exporter qw(import);\n"
|
|
"our @EXPORT = qw();\n"
|
|
"\n"
|
|
"use pgBackRest::Common::Exception;\n"
|
|
"use pgBackRest::Common::Log;\n"
|
|
"use pgBackRest::LibC qw(:random :encode);\n"
|
|
"\n\n\n\n"
|
|
"sub cipherPassGen\n"
|
|
"{\n"
|
|
"\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$iKeySizeInBytes,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '::cipherPassGen', \\@_,\n"
|
|
"{name => 'iKeySizeInBytes', default => 48},\n"
|
|
");\n"
|
|
"\n\n"
|
|
"my $strCipherPass = encodeToStr(ENCODE_TYPE_BASE64, cryptoRandomBytes($iKeySizeInBytes));\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'strCipherPass', value => $strCipherPass, redact => true}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n"
|
|
"push @EXPORT, qw(cipherPassGen);\n"
|
|
"\n"
|
|
"1;\n"
|
|
},
|
|
{
|
|
.name = "pgBackRest/Common/Exception.pm",
|
|
.data =
|
|
"\n\n\n"
|
|
"package pgBackRest::Common::Exception;\n"
|
|
"\n"
|
|
"use strict;\n"
|
|
"use warnings FATAL => qw(all);\n"
|
|
"use Carp qw(confess longmess);\n"
|
|
"\n"
|
|
"use Scalar::Util qw(blessed);\n"
|
|
"\n"
|
|
"use Exporter qw(import);\n"
|
|
"our @EXPORT = qw();\n"
|
|
"\n"
|
|
"use pgBackRest::Common::ExceptionAuto;\n"
|
|
"\n\n\n\n"
|
|
"push(@EXPORT, @pgBackRest::Common::ExceptionAuto::EXPORT);\n"
|
|
"\n\n\n\n"
|
|
"sub new\n"
|
|
"{\n"
|
|
"my $class = shift;\n"
|
|
"my $strLevel = shift;\n"
|
|
"my $iCode = shift;\n"
|
|
"my $strMessage = shift;\n"
|
|
"my $strTrace = shift;\n"
|
|
"my $rExtra = shift;\n"
|
|
"my $bErrorC = shift;\n"
|
|
"\n"
|
|
"if ($iCode < ERROR_MINIMUM || $iCode > ERROR_MAXIMUM)\n"
|
|
"{\n"
|
|
"$iCode = ERROR_INVALID;\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"my $self = {};\n"
|
|
"bless $self, $class;\n"
|
|
"\n\n"
|
|
"$self->{strLevel} = $strLevel;\n"
|
|
"$self->{iCode} = $iCode;\n"
|
|
"$self->{strMessage} = $strMessage;\n"
|
|
"$self->{strTrace} = $strTrace;\n"
|
|
"$self->{rExtra} = $rExtra;\n"
|
|
"$self->{bErrorC} = $bErrorC ? 1 : 0;\n"
|
|
"\n"
|
|
"return $self;\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub level\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n"
|
|
"return $self->{strLevel};\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub code\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n"
|
|
"return $self->{iCode};\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub errorC\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n"
|
|
"return $self->{bErrorC};\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub extra\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n"
|
|
"return $self->{rExtra};\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub message\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n"
|
|
"return $self->{strMessage};\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub trace\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n"
|
|
"return $self->{strTrace};\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub isException\n"
|
|
"{\n"
|
|
"my $roException = shift;\n"
|
|
"\n\n"
|
|
"if (defined($roException) && defined($$roException))\n"
|
|
"{\n"
|
|
"\n"
|
|
"if (blessed($$roException))\n"
|
|
"{\n"
|
|
"return $$roException->isa('pgBackRest::Common::Exception') ? 1 : 0;\n"
|
|
"}\n"
|
|
"\n"
|
|
"elsif ($$roException =~ /^PGBRCLIB\\:[0-9]+\\:/)\n"
|
|
"{\n"
|
|
"\n"
|
|
"my @stryException = split(/\\:/, $$roException);\n"
|
|
"shift(@stryException);\n"
|
|
"\n\n"
|
|
"my $iCode = shift(@stryException) + 0;\n"
|
|
"my $strTrace = shift(@stryException) . qw{:} . shift(@stryException);\n"
|
|
"my $strMessage = join(':', @stryException);\n"
|
|
"\n\n"
|
|
"$$roException = new pgBackRest::Common::Exception(\"ERROR\", $iCode, $strMessage, $strTrace, undef, 1);\n"
|
|
"\n"
|
|
"return 1;\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n"
|
|
"return 0;\n"
|
|
"}\n"
|
|
"\n"
|
|
"push @EXPORT, qw(isException);\n"
|
|
"\n\n\n\n\n\n"
|
|
"sub exceptionCode\n"
|
|
"{\n"
|
|
"my $oException = shift;\n"
|
|
"\n"
|
|
"return isException(\\$oException) ? $oException->code() : ERROR_UNKNOWN;\n"
|
|
"}\n"
|
|
"\n"
|
|
"push @EXPORT, qw(exceptionCode);\n"
|
|
"\n\n\n\n\n\n"
|
|
"sub exceptionMessage\n"
|
|
"{\n"
|
|
"my $oException = shift;\n"
|
|
"\n"
|
|
"return isException(\\$oException) ? $oException->message() : $oException;\n"
|
|
"}\n"
|
|
"\n"
|
|
"push @EXPORT, qw(exceptionMessage);\n"
|
|
"\n"
|
|
"1;\n"
|
|
},
|
|
{
|
|
.name = "pgBackRest/Common/ExceptionAuto.pm",
|
|
.data =
|
|
"\n\n\n\n\n"
|
|
"package pgBackRest::Common::ExceptionAuto;\n"
|
|
"\n"
|
|
"use strict;\n"
|
|
"use warnings FATAL => qw(all);\n"
|
|
"\n"
|
|
"use Exporter qw(import);\n"
|
|
"our @EXPORT = qw();\n"
|
|
"\n\n\n\n"
|
|
"use constant ERROR_MINIMUM => 25;\n"
|
|
"push @EXPORT, qw(ERROR_MINIMUM);\n"
|
|
"use constant ERROR_MAXIMUM => 125;\n"
|
|
"push @EXPORT, qw(ERROR_MAXIMUM);\n"
|
|
"\n"
|
|
"use constant ERROR_ASSERT => 25;\n"
|
|
"push @EXPORT, qw(ERROR_ASSERT);\n"
|
|
"use constant ERROR_CHECKSUM => 26;\n"
|
|
"push @EXPORT, qw(ERROR_CHECKSUM);\n"
|
|
"use constant ERROR_CONFIG => 27;\n"
|
|
"push @EXPORT, qw(ERROR_CONFIG);\n"
|
|
"use constant ERROR_FILE_INVALID => 28;\n"
|
|
"push @EXPORT, qw(ERROR_FILE_INVALID);\n"
|
|
"use constant ERROR_FORMAT => 29;\n"
|
|
"push @EXPORT, qw(ERROR_FORMAT);\n"
|
|
"use constant ERROR_COMMAND_REQUIRED => 30;\n"
|
|
"push @EXPORT, qw(ERROR_COMMAND_REQUIRED);\n"
|
|
"use constant ERROR_OPTION_INVALID => 31;\n"
|
|
"push @EXPORT, qw(ERROR_OPTION_INVALID);\n"
|
|
"use constant ERROR_OPTION_INVALID_VALUE => 32;\n"
|
|
"push @EXPORT, qw(ERROR_OPTION_INVALID_VALUE);\n"
|
|
"use constant ERROR_OPTION_INVALID_RANGE => 33;\n"
|
|
"push @EXPORT, qw(ERROR_OPTION_INVALID_RANGE);\n"
|
|
"use constant ERROR_OPTION_INVALID_PAIR => 34;\n"
|
|
"push @EXPORT, qw(ERROR_OPTION_INVALID_PAIR);\n"
|
|
"use constant ERROR_OPTION_DUPLICATE_KEY => 35;\n"
|
|
"push @EXPORT, qw(ERROR_OPTION_DUPLICATE_KEY);\n"
|
|
"use constant ERROR_OPTION_NEGATE => 36;\n"
|
|
"push @EXPORT, qw(ERROR_OPTION_NEGATE);\n"
|
|
"use constant ERROR_OPTION_REQUIRED => 37;\n"
|
|
"push @EXPORT, qw(ERROR_OPTION_REQUIRED);\n"
|
|
"use constant ERROR_POSTMASTER_RUNNING => 38;\n"
|
|
"push @EXPORT, qw(ERROR_POSTMASTER_RUNNING);\n"
|
|
"use constant ERROR_PROTOCOL => 39;\n"
|
|
"push @EXPORT, qw(ERROR_PROTOCOL);\n"
|
|
"use constant ERROR_PATH_NOT_EMPTY => 40;\n"
|
|
"push @EXPORT, qw(ERROR_PATH_NOT_EMPTY);\n"
|
|
"use constant ERROR_FILE_OPEN => 41;\n"
|
|
"push @EXPORT, qw(ERROR_FILE_OPEN);\n"
|
|
"use constant ERROR_FILE_READ => 42;\n"
|
|
"push @EXPORT, qw(ERROR_FILE_READ);\n"
|
|
"use constant ERROR_PARAM_REQUIRED => 43;\n"
|
|
"push @EXPORT, qw(ERROR_PARAM_REQUIRED);\n"
|
|
"use constant ERROR_ARCHIVE_MISMATCH => 44;\n"
|
|
"push @EXPORT, qw(ERROR_ARCHIVE_MISMATCH);\n"
|
|
"use constant ERROR_ARCHIVE_DUPLICATE => 45;\n"
|
|
"push @EXPORT, qw(ERROR_ARCHIVE_DUPLICATE);\n"
|
|
"use constant ERROR_VERSION_NOT_SUPPORTED => 46;\n"
|
|
"push @EXPORT, qw(ERROR_VERSION_NOT_SUPPORTED);\n"
|
|
"use constant ERROR_PATH_CREATE => 47;\n"
|
|
"push @EXPORT, qw(ERROR_PATH_CREATE);\n"
|
|
"use constant ERROR_COMMAND_INVALID => 48;\n"
|
|
"push @EXPORT, qw(ERROR_COMMAND_INVALID);\n"
|
|
"use constant ERROR_HOST_CONNECT => 49;\n"
|
|
"push @EXPORT, qw(ERROR_HOST_CONNECT);\n"
|
|
"use constant ERROR_LOCK_ACQUIRE => 50;\n"
|
|
"push @EXPORT, qw(ERROR_LOCK_ACQUIRE);\n"
|
|
"use constant ERROR_BACKUP_MISMATCH => 51;\n"
|
|
"push @EXPORT, qw(ERROR_BACKUP_MISMATCH);\n"
|
|
"use constant ERROR_FILE_SYNC => 52;\n"
|
|
"push @EXPORT, qw(ERROR_FILE_SYNC);\n"
|
|
"use constant ERROR_PATH_OPEN => 53;\n"
|
|
"push @EXPORT, qw(ERROR_PATH_OPEN);\n"
|
|
"use constant ERROR_PATH_SYNC => 54;\n"
|
|
"push @EXPORT, qw(ERROR_PATH_SYNC);\n"
|
|
"use constant ERROR_FILE_MISSING => 55;\n"
|
|
"push @EXPORT, qw(ERROR_FILE_MISSING);\n"
|
|
"use constant ERROR_DB_CONNECT => 56;\n"
|
|
"push @EXPORT, qw(ERROR_DB_CONNECT);\n"
|
|
"use constant ERROR_DB_QUERY => 57;\n"
|
|
"push @EXPORT, qw(ERROR_DB_QUERY);\n"
|
|
"use constant ERROR_DB_MISMATCH => 58;\n"
|
|
"push @EXPORT, qw(ERROR_DB_MISMATCH);\n"
|
|
"use constant ERROR_DB_TIMEOUT => 59;\n"
|
|
"push @EXPORT, qw(ERROR_DB_TIMEOUT);\n"
|
|
"use constant ERROR_FILE_REMOVE => 60;\n"
|
|
"push @EXPORT, qw(ERROR_FILE_REMOVE);\n"
|
|
"use constant ERROR_PATH_REMOVE => 61;\n"
|
|
"push @EXPORT, qw(ERROR_PATH_REMOVE);\n"
|
|
"use constant ERROR_STOP => 62;\n"
|
|
"push @EXPORT, qw(ERROR_STOP);\n"
|
|
"use constant ERROR_TERM => 63;\n"
|
|
"push @EXPORT, qw(ERROR_TERM);\n"
|
|
"use constant ERROR_FILE_WRITE => 64;\n"
|
|
"push @EXPORT, qw(ERROR_FILE_WRITE);\n"
|
|
"use constant ERROR_PROTOCOL_TIMEOUT => 66;\n"
|
|
"push @EXPORT, qw(ERROR_PROTOCOL_TIMEOUT);\n"
|
|
"use constant ERROR_FEATURE_NOT_SUPPORTED => 67;\n"
|
|
"push @EXPORT, qw(ERROR_FEATURE_NOT_SUPPORTED);\n"
|
|
"use constant ERROR_ARCHIVE_COMMAND_INVALID => 68;\n"
|
|
"push @EXPORT, qw(ERROR_ARCHIVE_COMMAND_INVALID);\n"
|
|
"use constant ERROR_LINK_EXPECTED => 69;\n"
|
|
"push @EXPORT, qw(ERROR_LINK_EXPECTED);\n"
|
|
"use constant ERROR_LINK_DESTINATION => 70;\n"
|
|
"push @EXPORT, qw(ERROR_LINK_DESTINATION);\n"
|
|
"use constant ERROR_TABLESPACE_IN_PGDATA => 71;\n"
|
|
"push @EXPORT, qw(ERROR_TABLESPACE_IN_PGDATA);\n"
|
|
"use constant ERROR_HOST_INVALID => 72;\n"
|
|
"push @EXPORT, qw(ERROR_HOST_INVALID);\n"
|
|
"use constant ERROR_PATH_MISSING => 73;\n"
|
|
"push @EXPORT, qw(ERROR_PATH_MISSING);\n"
|
|
"use constant ERROR_FILE_MOVE => 74;\n"
|
|
"push @EXPORT, qw(ERROR_FILE_MOVE);\n"
|
|
"use constant ERROR_BACKUP_SET_INVALID => 75;\n"
|
|
"push @EXPORT, qw(ERROR_BACKUP_SET_INVALID);\n"
|
|
"use constant ERROR_TABLESPACE_MAP => 76;\n"
|
|
"push @EXPORT, qw(ERROR_TABLESPACE_MAP);\n"
|
|
"use constant ERROR_PATH_TYPE => 77;\n"
|
|
"push @EXPORT, qw(ERROR_PATH_TYPE);\n"
|
|
"use constant ERROR_LINK_MAP => 78;\n"
|
|
"push @EXPORT, qw(ERROR_LINK_MAP);\n"
|
|
"use constant ERROR_FILE_CLOSE => 79;\n"
|
|
"push @EXPORT, qw(ERROR_FILE_CLOSE);\n"
|
|
"use constant ERROR_DB_MISSING => 80;\n"
|
|
"push @EXPORT, qw(ERROR_DB_MISSING);\n"
|
|
"use constant ERROR_DB_INVALID => 81;\n"
|
|
"push @EXPORT, qw(ERROR_DB_INVALID);\n"
|
|
"use constant ERROR_ARCHIVE_TIMEOUT => 82;\n"
|
|
"push @EXPORT, qw(ERROR_ARCHIVE_TIMEOUT);\n"
|
|
"use constant ERROR_FILE_MODE => 83;\n"
|
|
"push @EXPORT, qw(ERROR_FILE_MODE);\n"
|
|
"use constant ERROR_OPTION_MULTIPLE_VALUE => 84;\n"
|
|
"push @EXPORT, qw(ERROR_OPTION_MULTIPLE_VALUE);\n"
|
|
"use constant ERROR_PROTOCOL_OUTPUT_REQUIRED => 85;\n"
|
|
"push @EXPORT, qw(ERROR_PROTOCOL_OUTPUT_REQUIRED);\n"
|
|
"use constant ERROR_LINK_OPEN => 86;\n"
|
|
"push @EXPORT, qw(ERROR_LINK_OPEN);\n"
|
|
"use constant ERROR_ARCHIVE_DISABLED => 87;\n"
|
|
"push @EXPORT, qw(ERROR_ARCHIVE_DISABLED);\n"
|
|
"use constant ERROR_FILE_OWNER => 88;\n"
|
|
"push @EXPORT, qw(ERROR_FILE_OWNER);\n"
|
|
"use constant ERROR_USER_MISSING => 89;\n"
|
|
"push @EXPORT, qw(ERROR_USER_MISSING);\n"
|
|
"use constant ERROR_OPTION_COMMAND => 90;\n"
|
|
"push @EXPORT, qw(ERROR_OPTION_COMMAND);\n"
|
|
"use constant ERROR_GROUP_MISSING => 91;\n"
|
|
"push @EXPORT, qw(ERROR_GROUP_MISSING);\n"
|
|
"use constant ERROR_PATH_EXISTS => 92;\n"
|
|
"push @EXPORT, qw(ERROR_PATH_EXISTS);\n"
|
|
"use constant ERROR_FILE_EXISTS => 93;\n"
|
|
"push @EXPORT, qw(ERROR_FILE_EXISTS);\n"
|
|
"use constant ERROR_MEMORY => 94;\n"
|
|
"push @EXPORT, qw(ERROR_MEMORY);\n"
|
|
"use constant ERROR_CRYPTO => 95;\n"
|
|
"push @EXPORT, qw(ERROR_CRYPTO);\n"
|
|
"use constant ERROR_PARAM_INVALID => 96;\n"
|
|
"push @EXPORT, qw(ERROR_PARAM_INVALID);\n"
|
|
"use constant ERROR_PATH_CLOSE => 97;\n"
|
|
"push @EXPORT, qw(ERROR_PATH_CLOSE);\n"
|
|
"use constant ERROR_FILE_INFO => 98;\n"
|
|
"push @EXPORT, qw(ERROR_FILE_INFO);\n"
|
|
"use constant ERROR_JSON_FORMAT => 99;\n"
|
|
"push @EXPORT, qw(ERROR_JSON_FORMAT);\n"
|
|
"use constant ERROR_KERNEL => 100;\n"
|
|
"push @EXPORT, qw(ERROR_KERNEL);\n"
|
|
"use constant ERROR_SERVICE => 101;\n"
|
|
"push @EXPORT, qw(ERROR_SERVICE);\n"
|
|
"use constant ERROR_EXECUTE => 102;\n"
|
|
"push @EXPORT, qw(ERROR_EXECUTE);\n"
|
|
"use constant ERROR_RUNTIME => 122;\n"
|
|
"push @EXPORT, qw(ERROR_RUNTIME);\n"
|
|
"use constant ERROR_INVALID => 123;\n"
|
|
"push @EXPORT, qw(ERROR_INVALID);\n"
|
|
"use constant ERROR_UNHANDLED => 124;\n"
|
|
"push @EXPORT, qw(ERROR_UNHANDLED);\n"
|
|
"use constant ERROR_UNKNOWN => 125;\n"
|
|
"push @EXPORT, qw(ERROR_UNKNOWN);\n"
|
|
"\n"
|
|
"1;\n"
|
|
},
|
|
{
|
|
.name = "pgBackRest/Common/Http/Client.pm",
|
|
.data =
|
|
"\n\n\n"
|
|
"package pgBackRest::Common::Http::Client;\n"
|
|
"use parent 'pgBackRest::Common::Io::Buffered';\n"
|
|
"\n"
|
|
"use strict;\n"
|
|
"use warnings FATAL => qw(all);\n"
|
|
"use Carp qw(confess);\n"
|
|
"use English '-no_match_vars';\n"
|
|
"\n"
|
|
"use Exporter qw(import);\n"
|
|
"our @EXPORT = qw();\n"
|
|
"use IO::Socket::SSL;\n"
|
|
"use Socket qw(SOL_SOCKET SO_KEEPALIVE);\n"
|
|
"\n"
|
|
"use pgBackRest::Common::Exception;\n"
|
|
"use pgBackRest::Common::Io::Buffered;\n"
|
|
"use pgBackRest::Common::Log;\n"
|
|
"use pgBackRest::Common::String;\n"
|
|
"use pgBackRest::Common::Xml;\n"
|
|
"use pgBackRest::Common::Http::Common;\n"
|
|
"\n\n\n\n"
|
|
"use constant HTTP_VERB_GET => 'GET';\n"
|
|
"push @EXPORT, qw(HTTP_VERB_GET);\n"
|
|
"use constant HTTP_VERB_POST => 'POST';\n"
|
|
"push @EXPORT, qw(HTTP_VERB_POST);\n"
|
|
"use constant HTTP_VERB_PUT => 'PUT';\n"
|
|
"push @EXPORT, qw(HTTP_VERB_PUT);\n"
|
|
"\n"
|
|
"use constant HTTP_HEADER_CONTENT_LENGTH => 'content-length';\n"
|
|
"push @EXPORT, qw(HTTP_HEADER_CONTENT_LENGTH);\n"
|
|
"use constant HTTP_HEADER_TRANSFER_ENCODING => 'transfer-encoding';\n"
|
|
"push @EXPORT, qw(HTTP_HEADER_TRANSFER_ENCODING);\n"
|
|
"\n\n\n\n"
|
|
"sub new\n"
|
|
"{\n"
|
|
"my $class = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$strHost,\n"
|
|
"$strVerb,\n"
|
|
"$iPort,\n"
|
|
"$strUri,\n"
|
|
"$hQuery,\n"
|
|
"$hRequestHeader,\n"
|
|
"$rstrRequestBody,\n"
|
|
"$bResponseBodyPrefetch,\n"
|
|
"$iProtocolTimeout,\n"
|
|
"$iTryTotal,\n"
|
|
"$lBufferMax,\n"
|
|
"$bVerifySsl,\n"
|
|
"$strCaPath,\n"
|
|
"$strCaFile,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->new', \\@_,\n"
|
|
"{name => 'strHost', trace => true},\n"
|
|
"{name => 'strVerb', trace => true},\n"
|
|
"{name => 'iPort', optional => true, default => 443, trace => true},\n"
|
|
"{name => 'strUri', optional => true, default => qw(/), trace => true},\n"
|
|
"{name => 'hQuery', optional => true, trace => true},\n"
|
|
"{name => 'hRequestHeader', optional => true, trace => true},\n"
|
|
"{name => 'rstrRequestBody', optional => true, trace => true},\n"
|
|
"{name => 'bResponseBodyPrefetch', optional => true, default => false, trace => true},\n"
|
|
"{name => 'iProtocolTimeout', optional => true, default => 300, trace => true},\n"
|
|
"{name => 'iTryTotal', optional => true, default => 3, trace => true},\n"
|
|
"{name => 'lBufferMax', optional => true, default => 32768, trace => true},\n"
|
|
"{name => 'bVerifySsl', optional => true, default => true, trace => true},\n"
|
|
"{name => 'strCaPath', optional => true, trace => true},\n"
|
|
"{name => 'strCaFile', optional => true, trace => true},\n"
|
|
");\n"
|
|
"\n\n"
|
|
"my $self;\n"
|
|
"my $iTry = 1;\n"
|
|
"my $bRetry;\n"
|
|
"\n"
|
|
"do\n"
|
|
"{\n"
|
|
"\n"
|
|
"logDisable() if $iTry < $iTryTotal;\n"
|
|
"$bRetry = false;\n"
|
|
"\n"
|
|
"eval\n"
|
|
"{\n"
|
|
"\n"
|
|
"my $oSocket;\n"
|
|
"\n"
|
|
"if (eval{require IO::Socket::IP})\n"
|
|
"{\n"
|
|
"$oSocket = IO::Socket::IP->new(PeerHost => $strHost, PeerPort => $iPort)\n"
|
|
"or confess &log(ERROR, \"unable to create socket: $@\", ERROR_HOST_CONNECT);\n"
|
|
"}\n"
|
|
"else\n"
|
|
"{\n"
|
|
"require IO::Socket::INET;\n"
|
|
"\n"
|
|
"$oSocket = IO::Socket::INET->new(PeerHost => $strHost, PeerPort => $iPort)\n"
|
|
"or confess &log(ERROR, \"unable to create socket: $@\", ERROR_HOST_CONNECT);\n"
|
|
"}\n"
|
|
"\n"
|
|
"setsockopt($oSocket, SOL_SOCKET,SO_KEEPALIVE, 1)\n"
|
|
"or confess &log(ERROR, \"unable to set socket keepalive: $@\", ERROR_HOST_CONNECT);\n"
|
|
"\n"
|
|
"eval\n"
|
|
"{\n"
|
|
"IO::Socket::SSL->start_SSL(\n"
|
|
"$oSocket, SSL_verify_mode => $bVerifySsl ? SSL_VERIFY_PEER : SSL_VERIFY_NONE, SSL_ca_path => $strCaPath,\n"
|
|
"SSL_ca_file => $strCaFile);\n"
|
|
"}\n"
|
|
"or do\n"
|
|
"{\n"
|
|
"logErrorResult(\n"
|
|
"ERROR_HOST_CONNECT, coalesce(length($!) == 0 ? undef : $!, $SSL_ERROR), length($!) > 0 ? $SSL_ERROR : undef);\n"
|
|
"};\n"
|
|
"\n\n"
|
|
"$self = $class->SUPER::new(\n"
|
|
"new pgBackRest::Common::Io::Handle('httpClient', $oSocket, $oSocket), $iProtocolTimeout, $lBufferMax);\n"
|
|
"bless $self, $class;\n"
|
|
"\n\n"
|
|
"$self->{oSocket} = $oSocket;\n"
|
|
"\n\n"
|
|
"my $strQuery = httpQuery($hQuery);\n"
|
|
"\n\n"
|
|
"$self->{strRequestHeader} = \"${strVerb} \" . httpUriEncode($strUri, true) . \"?${strQuery} HTTP/1.1\" . \"\\r\\n\";\n"
|
|
"\n"
|
|
"foreach my $strHeader (sort(keys(%{$hRequestHeader})))\n"
|
|
"{\n"
|
|
"$self->{strRequestHeader} .= \"${strHeader}: $hRequestHeader->{$strHeader}\\r\\n\";\n"
|
|
"}\n"
|
|
"\n"
|
|
"$self->{strRequestHeader} .= \"\\r\\n\";\n"
|
|
"\n\n"
|
|
"$self->write(\\$self->{strRequestHeader});\n"
|
|
"\n\n"
|
|
"if (defined($rstrRequestBody))\n"
|
|
"{\n"
|
|
"my $iTotalSize = length($$rstrRequestBody);\n"
|
|
"my $iTotalSent = 0;\n"
|
|
"\n\n"
|
|
"do\n"
|
|
"{\n"
|
|
"my $strBufferWrite = substr($$rstrRequestBody, $iTotalSent, $lBufferMax);\n"
|
|
"$iTotalSent += $self->write(\\$strBufferWrite);\n"
|
|
"} while ($iTotalSent < $iTotalSize);\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"($self->{strResponseProtocol}, $self->{iResponseCode}, $self->{strResponseMessage}) =\n"
|
|
"split(' ', trim($self->readLine()));\n"
|
|
"\n\n"
|
|
"$self->{iContentLength} = 0;\n"
|
|
"$self->{strResponseHeader} = '';\n"
|
|
"my $strHeader = trim($self->readLine());\n"
|
|
"\n"
|
|
"while ($strHeader ne '')\n"
|
|
"{\n"
|
|
"\n"
|
|
"$self->{strResponseHeader} .= \"${strHeader}\\n\";\n"
|
|
"\n"
|
|
"my $iColonPos = index($strHeader, ':');\n"
|
|
"\n"
|
|
"if ($iColonPos == -1)\n"
|
|
"{\n"
|
|
"confess &log(ERROR, \"http header '${strHeader}' requires colon separator\", ERROR_PROTOCOL);\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"my $strHeaderKey = lc(substr($strHeader, 0, $iColonPos));\n"
|
|
"my $strHeaderValue = trim(substr($strHeader, $iColonPos + 1));\n"
|
|
"\n\n"
|
|
"$self->{hResponseHeader}{$strHeaderKey} = $strHeaderValue;\n"
|
|
"\n\n"
|
|
"if ($strHeaderKey eq HTTP_HEADER_CONTENT_LENGTH)\n"
|
|
"{\n"
|
|
"$self->{iContentLength} = $strHeaderValue + 0;\n"
|
|
"$self->{iContentRemaining} = $self->{iContentLength};\n"
|
|
"}\n"
|
|
"\n"
|
|
"elsif ($strHeaderKey eq HTTP_HEADER_TRANSFER_ENCODING)\n"
|
|
"{\n"
|
|
"if ($strHeaderValue eq 'chunked')\n"
|
|
"{\n"
|
|
"$self->{iContentLength} = -1;\n"
|
|
"}\n"
|
|
"else\n"
|
|
"{\n"
|
|
"confess &log(ERROR, \"invalid value '${strHeaderValue} for http header '${strHeaderKey}'\", ERROR_PROTOCOL);\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"$strHeader = trim($self->readLine());\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if ($bResponseBodyPrefetch)\n"
|
|
"{\n"
|
|
"$self->{strResponseBody} = $self->responseBody();\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"logEnable() if $iTry < $iTryTotal;\n"
|
|
"return 1;\n"
|
|
"}\n"
|
|
"or do\n"
|
|
"{\n"
|
|
"\n"
|
|
"logEnable() if $iTry < $iTryTotal;\n"
|
|
"\n\n"
|
|
"if ($iTry == $iTryTotal)\n"
|
|
"{\n"
|
|
"confess $EVAL_ERROR;\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"$iTry++;\n"
|
|
"$bRetry = true;\n"
|
|
"};\n"
|
|
"}\n"
|
|
"while ($bRetry);\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'self', value => $self}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub read\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"my $rtBuffer = shift;\n"
|
|
"my $iRequestSize = shift;\n"
|
|
"\n\n"
|
|
"$iRequestSize = $iRequestSize < $self->{iContentRemaining} ? $iRequestSize : $self->{iContentRemaining};\n"
|
|
"$self->{iContentRemaining} -= $iRequestSize;\n"
|
|
"\n"
|
|
"my $iActualSize = $self->SUPER::read($rtBuffer, $iRequestSize, true);\n"
|
|
"\n\n"
|
|
"if ($self->{iContentRemaining} == 0)\n"
|
|
"{\n"
|
|
"$self->SUPER::eofSet(true);\n"
|
|
"}\n"
|
|
"\n"
|
|
"return $iActualSize;\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub close\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"if (defined($self->{oSocket}))\n"
|
|
"{\n"
|
|
"$self->{oSocket}->close();\n"
|
|
"undef($self->{oSocket});\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n"
|
|
"sub DESTROY {shift->close()}\n"
|
|
"\n\n\n\n"
|
|
"sub responseBody\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->responseBody'\n"
|
|
");\n"
|
|
"\n\n"
|
|
"return $self->{strResponseBody} if exists($self->{strResponseBody});\n"
|
|
"\n\n"
|
|
"my $strResponseBody = undef;\n"
|
|
"\n"
|
|
"if ($self->{iContentLength} != 0)\n"
|
|
"{\n"
|
|
"\n"
|
|
"if ($self->{iContentLength} == -1)\n"
|
|
"{\n"
|
|
"while (1)\n"
|
|
"{\n"
|
|
"\n"
|
|
"my $strChunkLength = trim($self->readLine());\n"
|
|
"my $iChunkLength = hex($strChunkLength);\n"
|
|
"\n\n"
|
|
"last if ($iChunkLength == 0);\n"
|
|
"\n\n"
|
|
"$self->SUPER::read(\\$strResponseBody, $iChunkLength, true);\n"
|
|
"$self->readLine();\n"
|
|
"};\n"
|
|
"}\n"
|
|
"\n"
|
|
"else\n"
|
|
"{\n"
|
|
"$self->SUPER::read(\\$strResponseBody, $self->{iContentLength}, true);\n"
|
|
"}\n"
|
|
"\n"
|
|
"$self->close();\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'rstrResponseBody', value => \\$strResponseBody, trace => true}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub contentLength {shift->{iContentLength}}\n"
|
|
"sub requestHeaderText {trim(shift->{strRequestHeader})}\n"
|
|
"sub responseCode {shift->{iResponseCode}}\n"
|
|
"sub responseHeader {shift->{hResponseHeader}}\n"
|
|
"sub responseHeaderText {trim(shift->{strResponseHeader})}\n"
|
|
"sub responseMessage {shift->{strResponseMessage}}\n"
|
|
"sub responseProtocol {shift->{strResponseProtocol}}\n"
|
|
"\n"
|
|
"1;\n"
|
|
},
|
|
{
|
|
.name = "pgBackRest/Common/Http/Common.pm",
|
|
.data =
|
|
"\n\n\n"
|
|
"package pgBackRest::Common::Http::Common;\n"
|
|
"\n"
|
|
"use strict;\n"
|
|
"use warnings FATAL => qw(all);\n"
|
|
"use Carp qw(confess);\n"
|
|
"use English '-no_match_vars';\n"
|
|
"\n"
|
|
"use Exporter qw(import);\n"
|
|
"our @EXPORT = qw();\n"
|
|
"\n"
|
|
"use pgBackRest::Common::Exception;\n"
|
|
"use pgBackRest::Common::Log;\n"
|
|
"\n\n\n\n"
|
|
"sub httpQuery\n"
|
|
"{\n"
|
|
"\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$hQuery,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '::httpQuery', \\@_,\n"
|
|
"{name => 'hQuery', required => false, trace => true},\n"
|
|
");\n"
|
|
"\n\n"
|
|
"my $strQuery = '';\n"
|
|
"\n\n"
|
|
"if (ref($hQuery))\n"
|
|
"{\n"
|
|
"foreach my $strParam (sort(keys(%{$hQuery})))\n"
|
|
"{\n"
|
|
"\n"
|
|
"if (defined($hQuery->{$strParam}))\n"
|
|
"{\n"
|
|
"$strQuery .= ($strQuery eq '' ? '' : '&') . $strParam . '=' . httpUriEncode($hQuery->{$strParam});\n"
|
|
"}\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n"
|
|
"elsif (defined($hQuery))\n"
|
|
"{\n"
|
|
"$strQuery = $hQuery;\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'strQuery', value => $strQuery, trace => true}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n"
|
|
"push @EXPORT, qw(httpQuery);\n"
|
|
"\n\n\n\n"
|
|
"sub httpUriEncode\n"
|
|
"{\n"
|
|
"my $strString = shift;\n"
|
|
"my $bPath = shift;\n"
|
|
"\n\n"
|
|
"my $strEncodedString;\n"
|
|
"\n"
|
|
"if (defined($strString))\n"
|
|
"{\n"
|
|
"\n"
|
|
"for (my $iIndex = 0; $iIndex < length($strString); $iIndex++)\n"
|
|
"{\n"
|
|
"my $cChar = substr($strString, $iIndex, 1);\n"
|
|
"\n\n"
|
|
"if (($cChar ge 'A' && $cChar le 'Z') || ($cChar ge 'a' && $cChar le 'z') || ($cChar ge '0' && $cChar le '9') ||\n"
|
|
"$cChar eq '_' || $cChar eq '-' || $cChar eq '~' || $cChar eq '.' || ($bPath && $cChar eq '/'))\n"
|
|
"{\n"
|
|
"$strEncodedString .= $cChar;\n"
|
|
"}\n"
|
|
"\n"
|
|
"elsif ($cChar eq '/')\n"
|
|
"{\n"
|
|
"$strEncodedString .= '%2F';\n"
|
|
"}\n"
|
|
"\n"
|
|
"else\n"
|
|
"{\n"
|
|
"$strEncodedString .= sprintf('%%%02X', ord($cChar));\n"
|
|
"}\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n"
|
|
"return $strEncodedString;\n"
|
|
"}\n"
|
|
"\n"
|
|
"push @EXPORT, qw(httpUriEncode);\n"
|
|
"\n"
|
|
"1;\n"
|
|
},
|
|
{
|
|
.name = "pgBackRest/Common/Ini.pm",
|
|
.data =
|
|
"\n\n\n"
|
|
"package pgBackRest::Common::Ini;\n"
|
|
"\n"
|
|
"use strict;\n"
|
|
"use warnings FATAL => qw(all);\n"
|
|
"use Carp qw(confess);\n"
|
|
"use English '-no_match_vars';\n"
|
|
"\n"
|
|
"use Exporter qw(import);\n"
|
|
"our @EXPORT = qw();\n"
|
|
"use File::Basename qw(dirname);\n"
|
|
"use JSON::PP;\n"
|
|
"use Storable qw(dclone);\n"
|
|
"\n"
|
|
"use pgBackRest::Common::Exception;\n"
|
|
"use pgBackRest::Common::Log;\n"
|
|
"use pgBackRest::Common::String;\n"
|
|
"use pgBackRest::LibC qw(:crypto);\n"
|
|
"use pgBackRest::Version;\n"
|
|
"\n\n\n\n"
|
|
"use constant INI_TRUE => JSON::PP::true;\n"
|
|
"push @EXPORT, qw(INI_TRUE);\n"
|
|
"use constant INI_FALSE => JSON::PP::false;\n"
|
|
"push @EXPORT, qw(INI_FALSE);\n"
|
|
"\n\n\n\n"
|
|
"use constant INI_SECTION_BACKREST => 'backrest';\n"
|
|
"push @EXPORT, qw(INI_SECTION_BACKREST);\n"
|
|
"\n"
|
|
"use constant INI_KEY_CHECKSUM => 'backrest-checksum';\n"
|
|
"push @EXPORT, qw(INI_KEY_CHECKSUM);\n"
|
|
"use constant INI_KEY_FORMAT => 'backrest-format';\n"
|
|
"push @EXPORT, qw(INI_KEY_FORMAT);\n"
|
|
"use constant INI_KEY_VERSION => 'backrest-version';\n"
|
|
"push @EXPORT, qw(INI_KEY_VERSION);\n"
|
|
"\n"
|
|
"use constant INI_SECTION_CIPHER => 'cipher';\n"
|
|
"push @EXPORT, qw(INI_SECTION_CIPHER);\n"
|
|
"\n"
|
|
"use constant INI_KEY_CIPHER_PASS => 'cipher-pass';\n"
|
|
"push @EXPORT, qw(INI_KEY_CIPHER_PASS);\n"
|
|
"\n\n\n\n"
|
|
"use constant INI_COPY_EXT => '.copy';\n"
|
|
"push @EXPORT, qw(INI_COPY_EXT);\n"
|
|
"\n\n\n\n"
|
|
"use constant INI_SORT_FORWARD => 'forward';\n"
|
|
"push @EXPORT, qw(INI_SORT_FORWARD);\n"
|
|
"use constant INI_SORT_REVERSE => 'reverse';\n"
|
|
"push @EXPORT, qw(INI_SORT_REVERSE);\n"
|
|
"use constant INI_SORT_NONE => 'none';\n"
|
|
"push @EXPORT, qw(INI_SORT_NONE);\n"
|
|
"\n\n\n\n"
|
|
"sub new\n"
|
|
"{\n"
|
|
"my $class = shift;\n"
|
|
"\n\n"
|
|
"my $self = {};\n"
|
|
"bless $self, $class;\n"
|
|
"\n\n"
|
|
"require pgBackRest::Storage::Helper;\n"
|
|
"pgBackRest::Storage::Helper->import();\n"
|
|
"\n\n"
|
|
"(\n"
|
|
"my $strOperation,\n"
|
|
"$self->{strFileName},\n"
|
|
"my $bLoad,\n"
|
|
"my $strContent,\n"
|
|
"$self->{oStorage},\n"
|
|
"$self->{iInitFormat},\n"
|
|
"$self->{strInitVersion},\n"
|
|
"my $bIgnoreMissing,\n"
|
|
"$self->{strCipherPass},\n"
|
|
"my $strCipherPassSub,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->new', \\@_,\n"
|
|
"{name => 'strFileName', trace => true},\n"
|
|
"{name => 'bLoad', optional => true, default => true, trace => true},\n"
|
|
"{name => 'strContent', optional => true, trace => true},\n"
|
|
"{name => 'oStorage', optional => true, default => storageLocal(), trace => true},\n"
|
|
"{name => 'iInitFormat', optional => true, default => REPOSITORY_FORMAT, trace => true},\n"
|
|
"{name => 'strInitVersion', optional => true, default => PROJECT_VERSION, trace => true},\n"
|
|
"{name => 'bIgnoreMissing', optional => true, default => false, trace => true},\n"
|
|
"{name => 'strCipherPass', optional => true, trace => true},\n"
|
|
"{name => 'strCipherPassSub', optional => true, trace => true},\n"
|
|
");\n"
|
|
"\n"
|
|
"if (defined($self->{oStorage}->cipherPassUser()) && !defined($self->{strCipherPass}))\n"
|
|
"{\n"
|
|
"confess &log(ERROR, 'passphrase is required when storage is encrypted', ERROR_CRYPTO);\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"$self->{bModified} = false;\n"
|
|
"\n\n"
|
|
"$self->{bExists} = false;\n"
|
|
"\n\n"
|
|
"if ($bLoad)\n"
|
|
"{\n"
|
|
"$self->load($bIgnoreMissing);\n"
|
|
"}\n"
|
|
"\n"
|
|
"elsif (defined($strContent))\n"
|
|
"{\n"
|
|
"$self->{oContent} = iniParse($strContent);\n"
|
|
"$self->headerCheck();\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if (!$self->{bExists} && !defined($strContent))\n"
|
|
"{\n"
|
|
"$self->numericSet(INI_SECTION_BACKREST, INI_KEY_FORMAT, undef, $self->{iInitFormat});\n"
|
|
"$self->set(INI_SECTION_BACKREST, INI_KEY_VERSION, undef, $self->{strInitVersion});\n"
|
|
"\n\n"
|
|
"if (defined($self->{strCipherPass}) && defined($strCipherPassSub))\n"
|
|
"{\n"
|
|
"$self->set(INI_SECTION_CIPHER, INI_KEY_CIPHER_PASS, undef, $strCipherPassSub);\n"
|
|
"}\n"
|
|
"elsif ((defined($self->{strCipherPass}) && !defined($strCipherPassSub)) ||\n"
|
|
"(!defined($self->{strCipherPass}) && defined($strCipherPassSub)))\n"
|
|
"{\n"
|
|
"confess &log(ASSERT, 'a user passphrase and sub passphrase are both required when encrypting');\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n"
|
|
"return $self;\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub loadVersion\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"my $bCopy = shift;\n"
|
|
"my $bIgnoreError = shift;\n"
|
|
"\n\n"
|
|
"if ($self->{oStorage}->encryptionValid($self->{oStorage}->encrypted($self->{strFileName} . ($bCopy ? INI_COPY_EXT : ''),\n"
|
|
"{bIgnoreMissing => $bIgnoreError})))\n"
|
|
"{\n"
|
|
"\n"
|
|
"my $rstrContent = $self->{oStorage}->get(\n"
|
|
"$self->{oStorage}->openRead($self->{strFileName} . ($bCopy ? INI_COPY_EXT : ''),\n"
|
|
"{bIgnoreMissing => $bIgnoreError, strCipherPass => $self->{strCipherPass}}));\n"
|
|
"\n\n"
|
|
"if (defined($rstrContent))\n"
|
|
"{\n"
|
|
"my $rhContent = iniParse($$rstrContent, {bIgnoreInvalid => $bIgnoreError});\n"
|
|
"\n\n"
|
|
"if (defined($rhContent))\n"
|
|
"{\n"
|
|
"$self->{oContent} = $rhContent;\n"
|
|
"\n\n"
|
|
"if (!$self->headerCheck({bIgnoreInvalid => $bIgnoreError}))\n"
|
|
"{\n"
|
|
"delete($self->{oContent});\n"
|
|
"}\n"
|
|
"}\n"
|
|
"}\n"
|
|
"}\n"
|
|
"else\n"
|
|
"{\n"
|
|
"confess &log(ERROR, \"unable to parse '$self->{strFileName}\" . ($bCopy ? INI_COPY_EXT : '') . \"'\" .\n"
|
|
"\"\\nHINT: Is or was the repo encrypted?\", ERROR_CRYPTO);\n"
|
|
"}\n"
|
|
"\n"
|
|
"return defined($self->{oContent});\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub load\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"my $bIgnoreMissing = shift;\n"
|
|
"\n\n"
|
|
"if (!$self->loadVersion(false, true))\n"
|
|
"{\n"
|
|
"if (!$self->loadVersion(true, true))\n"
|
|
"{\n"
|
|
"return if $bIgnoreMissing;\n"
|
|
"\n"
|
|
"confess &log(ERROR, \"unable to open $self->{strFileName} or $self->{strFileName}\" . INI_COPY_EXT, ERROR_FILE_MISSING);\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n"
|
|
"$self->{bExists} = true;\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub headerCheck\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$bIgnoreInvalid,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->headerCheck', \\@_,\n"
|
|
"{name => 'bIgnoreInvalid', optional => true, default => false, trace => true},\n"
|
|
");\n"
|
|
"\n\n"
|
|
"my $bValid = true;\n"
|
|
"\n"
|
|
"eval\n"
|
|
"{\n"
|
|
"\n\n"
|
|
"my $strChecksum = $self->get(INI_SECTION_BACKREST, INI_KEY_CHECKSUM, undef, false);\n"
|
|
"my $strTestChecksum = $self->hash();\n"
|
|
"\n"
|
|
"if (!defined($strChecksum) || $strChecksum ne $strTestChecksum)\n"
|
|
"{\n"
|
|
"confess &log(ERROR,\n"
|
|
"\"invalid checksum in '$self->{strFileName}', expected '${strTestChecksum}' but found \" .\n"
|
|
"(defined($strChecksum) ? \"'${strChecksum}'\" : '[undef]'),\n"
|
|
"ERROR_CHECKSUM);\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"my $iFormat = $self->get(INI_SECTION_BACKREST, INI_KEY_FORMAT, undef, false, 0);\n"
|
|
"\n"
|
|
"if ($iFormat != $self->{iInitFormat})\n"
|
|
"{\n"
|
|
"confess &log(ERROR,\n"
|
|
"\"invalid format in '$self->{strFileName}', expected $self->{iInitFormat} but found ${iFormat}\", ERROR_FORMAT);\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if (!$self->test(INI_SECTION_BACKREST, INI_KEY_VERSION, undef, $self->{strInitVersion}))\n"
|
|
"{\n"
|
|
"$self->set(INI_SECTION_BACKREST, INI_KEY_VERSION, undef, $self->{strInitVersion});\n"
|
|
"}\n"
|
|
"\n"
|
|
"return true;\n"
|
|
"}\n"
|
|
"or do\n"
|
|
"{\n"
|
|
"\n"
|
|
"if (!$bIgnoreInvalid)\n"
|
|
"{\n"
|
|
"confess $EVAL_ERROR;\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"$bValid = false;\n"
|
|
"};\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'bValid', value => $bValid, trace => true}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"push @EXPORT, qw(iniParse);\n"
|
|
"\n"
|
|
"sub iniParse\n"
|
|
"{\n"
|
|
"\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$strContent,\n"
|
|
"$bRelaxed,\n"
|
|
"$bIgnoreInvalid,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '::iniParse', \\@_,\n"
|
|
"{name => 'strContent', required => false, trace => true},\n"
|
|
"{name => 'bRelaxed', optional => true, default => false, trace => true},\n"
|
|
"{name => 'bIgnoreInvalid', optional => true, default => false, trace => true},\n"
|
|
");\n"
|
|
"\n\n"
|
|
"my $oContent = undef;\n"
|
|
"my $strSection;\n"
|
|
"\n\n"
|
|
"my $oJSON = JSON::PP->new()->allow_nonref();\n"
|
|
"\n\n"
|
|
"eval\n"
|
|
"{\n"
|
|
"\n"
|
|
"foreach my $strLine (split(\"\\n\", defined($strContent) ? $strContent : ''))\n"
|
|
"{\n"
|
|
"$strLine = trim($strLine);\n"
|
|
"\n\n"
|
|
"if ($strLine ne '' && $strLine !~ '^[ ]*#.*')\n"
|
|
"{\n"
|
|
"\n"
|
|
"if (index($strLine, '[') == 0)\n"
|
|
"{\n"
|
|
"$strSection = substr($strLine, 1, length($strLine) - 2);\n"
|
|
"}\n"
|
|
"else\n"
|
|
"{\n"
|
|
"if (!defined($strSection))\n"
|
|
"{\n"
|
|
"confess &log(ERROR, \"key/value pair '${strLine}' found outside of a section\", ERROR_CONFIG);\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"my $iIndex = index($strLine, '=');\n"
|
|
"\n"
|
|
"if ($iIndex == -1)\n"
|
|
"{\n"
|
|
"confess &log(ERROR, \"unable to find '=' in '${strLine}'\", ERROR_CONFIG);\n"
|
|
"}\n"
|
|
"\n"
|
|
"my $strKey = substr($strLine, 0, $iIndex);\n"
|
|
"my $strValue = substr($strLine, $iIndex + 1);\n"
|
|
"\n\n"
|
|
"if ($bRelaxed)\n"
|
|
"{\n"
|
|
"if (defined($oContent->{$strSection}{$strKey}))\n"
|
|
"{\n"
|
|
"if (ref($oContent->{$strSection}{$strKey}) ne 'ARRAY')\n"
|
|
"{\n"
|
|
"$oContent->{$strSection}{$strKey} = [$oContent->{$strSection}{$strKey}];\n"
|
|
"}\n"
|
|
"\n"
|
|
"push(@{$oContent->{$strSection}{$strKey}}, $strValue);\n"
|
|
"}\n"
|
|
"else\n"
|
|
"{\n"
|
|
"$oContent->{$strSection}{$strKey} = $strValue;\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n"
|
|
"else\n"
|
|
"{\n"
|
|
"${$oContent}{$strSection}{$strKey} = $oJSON->decode($strValue);\n"
|
|
"}\n"
|
|
"}\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if (!($bRelaxed || defined($oContent)))\n"
|
|
"{\n"
|
|
"confess &log(ERROR, 'no key/value pairs found', ERROR_CONFIG);\n"
|
|
"}\n"
|
|
"\n"
|
|
"return true;\n"
|
|
"}\n"
|
|
"or do\n"
|
|
"{\n"
|
|
"\n"
|
|
"if (!$bIgnoreInvalid)\n"
|
|
"{\n"
|
|
"confess $EVAL_ERROR;\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"undef($oContent);\n"
|
|
"};\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'oContent', value => $oContent, trace => true}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub save\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"if ($self->{bModified})\n"
|
|
"{\n"
|
|
"\n"
|
|
"$self->hash();\n"
|
|
"\n\n"
|
|
"$self->{oStorage}->put($self->{strFileName}, iniRender($self->{oContent}), {strCipherPass => $self->{strCipherPass}});\n"
|
|
"$self->{oStorage}->pathSync(dirname($self->{strFileName}));\n"
|
|
"$self->{oStorage}->put($self->{strFileName} . INI_COPY_EXT, iniRender($self->{oContent}),\n"
|
|
"{strCipherPass => $self->{strCipherPass}});\n"
|
|
"$self->{oStorage}->pathSync(dirname($self->{strFileName}));\n"
|
|
"$self->{bModified} = false;\n"
|
|
"\n\n"
|
|
"$self->{bExists} = true;\n"
|
|
"\n\n"
|
|
"return true;\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"return false;\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub saveCopy\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n"
|
|
"if ($self->{oStorage}->exists($self->{strFileName}))\n"
|
|
"{\n"
|
|
"confess &log(ASSERT, \"cannot save copy only when '$self->{strFileName}' exists\");\n"
|
|
"}\n"
|
|
"\n"
|
|
"$self->hash();\n"
|
|
"$self->{oStorage}->put($self->{strFileName} . INI_COPY_EXT, iniRender($self->{oContent}),\n"
|
|
"{strCipherPass => $self->{strCipherPass}});\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"push @EXPORT, qw(iniRender);\n"
|
|
"\n"
|
|
"sub iniRender\n"
|
|
"{\n"
|
|
"\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$oContent,\n"
|
|
"$bRelaxed,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '::iniRender', \\@_,\n"
|
|
"{name => 'oContent', trace => true},\n"
|
|
"{name => 'bRelaxed', default => false, trace => true},\n"
|
|
");\n"
|
|
"\n\n"
|
|
"my $strContent = '';\n"
|
|
"my $bFirst = true;\n"
|
|
"\n\n"
|
|
"my $oJSON = JSON::PP->new()->canonical()->allow_nonref();\n"
|
|
"\n\n"
|
|
"foreach my $strSection (sort(keys(%$oContent)))\n"
|
|
"{\n"
|
|
"\n"
|
|
"if (!$bFirst)\n"
|
|
"{\n"
|
|
"$strContent .= \"\\n\";\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"$strContent .= \"[${strSection}]\\n\";\n"
|
|
"\n\n"
|
|
"foreach my $strKey (sort(keys(%{$oContent->{$strSection}})))\n"
|
|
"{\n"
|
|
"\n"
|
|
"my $strValue = ${$oContent}{$strSection}{$strKey};\n"
|
|
"\n\n"
|
|
"if ($bRelaxed)\n"
|
|
"{\n"
|
|
"\n"
|
|
"if (ref($strValue) eq 'ARRAY')\n"
|
|
"{\n"
|
|
"foreach my $strArrayValue (@{$strValue})\n"
|
|
"{\n"
|
|
"$strContent .= \"${strKey}=${strArrayValue}\\n\";\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n"
|
|
"else\n"
|
|
"{\n"
|
|
"$strContent .= \"${strKey}=${strValue}\\n\";\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n"
|
|
"else\n"
|
|
"{\n"
|
|
"$strContent .= \"${strKey}=\" . $oJSON->encode($strValue) . \"\\n\";\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n"
|
|
"$bFirst = false;\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'strContent', value => $strContent, trace => true}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub hash\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"delete($self->{oContent}{&INI_SECTION_BACKREST}{&INI_KEY_CHECKSUM});\n"
|
|
"\n\n"
|
|
"$self->{oContent}{&INI_SECTION_BACKREST}{&INI_KEY_CHECKSUM} =\n"
|
|
"cryptoHashOne('sha1', JSON::PP->new()->canonical()->allow_nonref()->encode($self->{oContent}));\n"
|
|
"\n"
|
|
"return $self->{oContent}{&INI_SECTION_BACKREST}{&INI_KEY_CHECKSUM};\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub get\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"my $strSection = shift;\n"
|
|
"my $strKey = shift;\n"
|
|
"my $strSubKey = shift;\n"
|
|
"my $bRequired = shift;\n"
|
|
"my $oDefault = shift;\n"
|
|
"\n\n"
|
|
"if (!defined($strSection))\n"
|
|
"{\n"
|
|
"confess &log(ASSERT, 'strSection is required');\n"
|
|
"}\n"
|
|
"\n"
|
|
"if (defined($strSubKey) && !defined($strKey))\n"
|
|
"{\n"
|
|
"confess &log(ASSERT, \"strKey is required when strSubKey '${strSubKey}' is requested\");\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"my $oResult = $self->{oContent}->{$strSection};\n"
|
|
"\n"
|
|
"if (defined($strKey) && defined($oResult))\n"
|
|
"{\n"
|
|
"$oResult = $oResult->{$strKey};\n"
|
|
"\n"
|
|
"if (defined($strSubKey) && defined($oResult))\n"
|
|
"{\n"
|
|
"$oResult = $oResult->{$strSubKey};\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if (!defined($oResult))\n"
|
|
"{\n"
|
|
"\n"
|
|
"if (!defined($bRequired) || $bRequired)\n"
|
|
"{\n"
|
|
"confess &log(ASSERT, \"strSection '$strSection'\" . (defined($strKey) ? \", strKey '$strKey'\" : '') .\n"
|
|
"(defined($strSubKey) ? \", strSubKey '$strSubKey'\" : '') . ' is required but not defined');\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if (defined($oDefault))\n"
|
|
"{\n"
|
|
"return $oDefault;\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n"
|
|
"return $oResult\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub boolGet\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"my $strSection = shift;\n"
|
|
"my $strKey = shift;\n"
|
|
"my $strSubKey = shift;\n"
|
|
"my $bRequired = shift;\n"
|
|
"my $bDefault = shift;\n"
|
|
"\n"
|
|
"return $self->get(\n"
|
|
"$strSection, $strKey, $strSubKey, $bRequired,\n"
|
|
"defined($bDefault) ? ($bDefault ? INI_TRUE : INI_FALSE) : undef) ? true : false;\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub numericGet\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"my $strSection = shift;\n"
|
|
"my $strKey = shift;\n"
|
|
"my $strSubKey = shift;\n"
|
|
"my $bRequired = shift;\n"
|
|
"my $nDefault = shift;\n"
|
|
"\n"
|
|
"return $self->get($strSection, $strKey, $strSubKey, $bRequired, defined($nDefault) ? $nDefault + 0 : undef) + 0;\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub set\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"my $strSection = shift;\n"
|
|
"my $strKey = shift;\n"
|
|
"my $strSubKey = shift;\n"
|
|
"my $oValue = shift;\n"
|
|
"\n\n"
|
|
"if (!(defined($strSection) && defined($strKey)))\n"
|
|
"{\n"
|
|
"confess &log(ASSERT, 'strSection and strKey are required');\n"
|
|
"}\n"
|
|
"\n"
|
|
"my $oCurrentValue;\n"
|
|
"\n"
|
|
"if (defined($strSubKey))\n"
|
|
"{\n"
|
|
"$oCurrentValue = \\$self->{oContent}{$strSection}{$strKey}{$strSubKey};\n"
|
|
"}\n"
|
|
"else\n"
|
|
"{\n"
|
|
"$oCurrentValue = \\$self->{oContent}{$strSection}{$strKey};\n"
|
|
"}\n"
|
|
"\n"
|
|
"if (!defined($$oCurrentValue) ||\n"
|
|
"defined($oCurrentValue) != defined($oValue) ||\n"
|
|
"${dclone($oCurrentValue)} ne ${dclone(\\$oValue)})\n"
|
|
"{\n"
|
|
"$$oCurrentValue = $oValue;\n"
|
|
"\n"
|
|
"if (!$self->{bModified})\n"
|
|
"{\n"
|
|
"$self->{bModified} = true;\n"
|
|
"}\n"
|
|
"\n"
|
|
"return true;\n"
|
|
"}\n"
|
|
"\n"
|
|
"return false;\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub boolSet\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"my $strSection = shift;\n"
|
|
"my $strKey = shift;\n"
|
|
"my $strSubKey = shift;\n"
|
|
"my $bValue = shift;\n"
|
|
"\n"
|
|
"$self->set($strSection, $strKey, $strSubKey, $bValue ? INI_TRUE : INI_FALSE);\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub numericSet\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"my $strSection = shift;\n"
|
|
"my $strKey = shift;\n"
|
|
"my $strSubKey = shift;\n"
|
|
"my $nValue = shift;\n"
|
|
"\n"
|
|
"$self->set($strSection, $strKey, $strSubKey, defined($nValue) ? $nValue + 0 : undef);\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub remove\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"my $strSection = shift;\n"
|
|
"my $strKey = shift;\n"
|
|
"my $strSubKey = shift;\n"
|
|
"\n\n"
|
|
"if ($self->test($strSection, $strKey, $strSubKey))\n"
|
|
"{\n"
|
|
"\n"
|
|
"if (defined($strSubKey))\n"
|
|
"{\n"
|
|
"delete($self->{oContent}{$strSection}{$strKey}{$strSubKey});\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if (defined($strKey))\n"
|
|
"{\n"
|
|
"if (!defined($strSubKey))\n"
|
|
"{\n"
|
|
"delete($self->{oContent}{$strSection}{$strKey});\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if (keys(%{$self->{oContent}{$strSection}}) == 0)\n"
|
|
"{\n"
|
|
"delete($self->{oContent}{$strSection});\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if (!defined($strKey))\n"
|
|
"{\n"
|
|
"delete($self->{oContent}{$strSection});\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if (!$self->{bModified})\n"
|
|
"{\n"
|
|
"$self->{bModified} = true;\n"
|
|
"}\n"
|
|
"\n"
|
|
"return true;\n"
|
|
"}\n"
|
|
"\n"
|
|
"return false;\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub keys\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"my $strSection = shift;\n"
|
|
"my $strSortOrder = shift;\n"
|
|
"\n"
|
|
"if ($self->test($strSection))\n"
|
|
"{\n"
|
|
"if (!defined($strSortOrder) || $strSortOrder eq INI_SORT_FORWARD)\n"
|
|
"{\n"
|
|
"return (sort(keys(%{$self->get($strSection)})));\n"
|
|
"}\n"
|
|
"elsif ($strSortOrder eq INI_SORT_REVERSE)\n"
|
|
"{\n"
|
|
"return (sort {$b cmp $a} (keys(%{$self->get($strSection)})));\n"
|
|
"}\n"
|
|
"elsif ($strSortOrder eq INI_SORT_NONE)\n"
|
|
"{\n"
|
|
"return (keys(%{$self->get($strSection)}));\n"
|
|
"}\n"
|
|
"else\n"
|
|
"{\n"
|
|
"confess &log(ASSERT, \"invalid strSortOrder '${strSortOrder}'\");\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n"
|
|
"my @stryEmptyArray;\n"
|
|
"return @stryEmptyArray;\n"
|
|
"}\n"
|
|
"\n\n\n\n\n\n\n"
|
|
"sub test\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"my $strSection = shift;\n"
|
|
"my $strValue = shift;\n"
|
|
"my $strSubValue = shift;\n"
|
|
"my $strTest = shift;\n"
|
|
"\n\n"
|
|
"my $strResult = $self->get($strSection, $strValue, $strSubValue, false);\n"
|
|
"\n\n"
|
|
"if (defined($strResult))\n"
|
|
"{\n"
|
|
"\n"
|
|
"if (defined($strTest))\n"
|
|
"{\n"
|
|
"\n"
|
|
"return ($strResult . '') eq ($strTest . '') ? true : false;\n"
|
|
"}\n"
|
|
"\n"
|
|
"return true;\n"
|
|
"}\n"
|
|
"\n"
|
|
"return false;\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub boolTest\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"my $strSection = shift;\n"
|
|
"my $strValue = shift;\n"
|
|
"my $strSubValue = shift;\n"
|
|
"my $bTest = shift;\n"
|
|
"\n"
|
|
"return $self->test($strSection, $strValue, $strSubValue, defined($bTest) ? ($bTest ? INI_TRUE : INI_FALSE) : undef);\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub cipherPassSub\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n"
|
|
"return $self->get(INI_SECTION_CIPHER, INI_KEY_CIPHER_PASS, undef, false);\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub modified {shift->{bModified}}\n"
|
|
"sub exists {shift->{bExists}}\n"
|
|
"sub cipherPass {shift->{strCipherPass}}\n"
|
|
"\n"
|
|
"1;\n"
|
|
},
|
|
{
|
|
.name = "pgBackRest/Common/Io/Base.pm",
|
|
.data =
|
|
"\n\n\n"
|
|
"package pgBackRest::Common::Io::Base;\n"
|
|
"\n"
|
|
"use strict;\n"
|
|
"use warnings FATAL => qw(all);\n"
|
|
"use Carp qw(confess);\n"
|
|
"use English '-no_match_vars';\n"
|
|
"\n"
|
|
"use Exporter qw(import);\n"
|
|
"our @EXPORT = qw();\n"
|
|
"use Scalar::Util qw(blessed);\n"
|
|
"\n"
|
|
"use pgBackRest::Common::Log;\n"
|
|
"\n\n\n\n"
|
|
"use constant COMMON_IO_BASE => __PACKAGE__;\n"
|
|
"push @EXPORT, qw(COMMON_IO_BASE);\n"
|
|
"\n\n\n\n"
|
|
"use constant COMMON_IO_BUFFER_MAX => 4194304;\n"
|
|
"push @EXPORT, qw(COMMON_IO_BUFFER_MAX);\n"
|
|
"\n\n\n\n"
|
|
"sub new\n"
|
|
"{\n"
|
|
"my $class = shift;\n"
|
|
"\n\n"
|
|
"my $self = {};\n"
|
|
"bless $self, $class;\n"
|
|
"\n\n"
|
|
"(\n"
|
|
"my $strOperation,\n"
|
|
"$self->{strId},\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->new', \\@_,\n"
|
|
"{name => 'strId', trace => true},\n"
|
|
");\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'self', value => $self}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub error\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"my $iCode = shift;\n"
|
|
"my $strMessage = shift;\n"
|
|
"my $strDetail = shift;\n"
|
|
"\n"
|
|
"logErrorResult($iCode, $strMessage, $strDetail);\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub result\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"my $strModule = shift;\n"
|
|
"\n"
|
|
"if (!defined($strModule))\n"
|
|
"{\n"
|
|
"return $self->{rhResult};\n"
|
|
"}\n"
|
|
"\n"
|
|
"return $self->{rhResult}{$strModule};\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub resultAll\n"
|
|
"{\n"
|
|
"shift->{rhResult};\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub resultSet\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"my $strModule = shift;\n"
|
|
"my $xResult = shift;\n"
|
|
"\n"
|
|
"$self->{rhResult}{$strModule} = $xResult;\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub className {blessed(shift)}\n"
|
|
"sub id {shift->{strId}}\n"
|
|
"\n"
|
|
"1;\n"
|
|
},
|
|
{
|
|
.name = "pgBackRest/Common/Io/Buffered.pm",
|
|
.data =
|
|
"\n\n\n"
|
|
"package pgBackRest::Common::Io::Buffered;\n"
|
|
"use parent 'pgBackRest::Common::Io::Filter';\n"
|
|
"\n"
|
|
"use strict;\n"
|
|
"use warnings FATAL => qw(all);\n"
|
|
"use Carp qw(confess);\n"
|
|
"use English '-no_match_vars';\n"
|
|
"\n"
|
|
"use Exporter qw(import);\n"
|
|
"our @EXPORT = qw();\n"
|
|
"use IO::Select;\n"
|
|
"use Time::HiRes qw(gettimeofday);\n"
|
|
"\n"
|
|
"use pgBackRest::Common::Exception;\n"
|
|
"use pgBackRest::Common::Io::Base;\n"
|
|
"use pgBackRest::Common::Io::Handle;\n"
|
|
"use pgBackRest::Common::Log;\n"
|
|
"use pgBackRest::Common::Wait;\n"
|
|
"\n\n\n\n"
|
|
"use constant COMMON_IO_BUFFERED => __PACKAGE__;\n"
|
|
"push @EXPORT, qw(COMMON_IO_BUFFERED);\n"
|
|
"\n\n\n\n"
|
|
"sub new\n"
|
|
"{\n"
|
|
"my $class = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$oParent,\n"
|
|
"$iTimeout,\n"
|
|
"$lBufferMax,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->new', \\@_,\n"
|
|
"{name => 'oParent', trace => true},\n"
|
|
"{name => 'iTimeout', default => 0, trace => true},\n"
|
|
"{name => 'lBufferMax', default => COMMON_IO_BUFFER_MAX, trace => true},\n"
|
|
");\n"
|
|
"\n\n"
|
|
"my $self = $class->SUPER::new($oParent);\n"
|
|
"bless $self, $class;\n"
|
|
"\n\n"
|
|
"$self->handleReadSet($self->handleRead());\n"
|
|
"\n\n"
|
|
"$self->{iTimeout} = $iTimeout;\n"
|
|
"$self->{lBufferMax} = $lBufferMax;\n"
|
|
"\n\n"
|
|
"$self->{tBuffer} = '';\n"
|
|
"$self->{lBufferSize} = 0;\n"
|
|
"$self->{lBufferPos} = 0;\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'self', value => $self}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub read\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"my $tBufferRef = shift;\n"
|
|
"my $iRequestSize = shift;\n"
|
|
"my $bBlock = shift;\n"
|
|
"\n\n"
|
|
"my $iRemainingSize = $iRequestSize;\n"
|
|
"\n\n"
|
|
"my $lBufferRemaining = $self->{lBufferSize} - $self->{lBufferPos};\n"
|
|
"\n"
|
|
"if ($lBufferRemaining > 0)\n"
|
|
"{\n"
|
|
"my $iReadSize = $iRequestSize < $lBufferRemaining ? $iRequestSize : $lBufferRemaining;\n"
|
|
"\n"
|
|
"$$tBufferRef .= substr($self->{tBuffer}, $self->{lBufferPos}, $iReadSize);\n"
|
|
"$self->{lBufferPos} += $iReadSize;\n"
|
|
"\n"
|
|
"$iRemainingSize -= $iReadSize;\n"
|
|
"}\n"
|
|
"\n\n\n"
|
|
"my $fTimeStart = gettimeofday();\n"
|
|
"my $fRemaining = $self->timeout();\n"
|
|
"\n"
|
|
"while ($iRemainingSize > 0 && $fRemaining > 0)\n"
|
|
"{\n"
|
|
"\n"
|
|
"if ($self->pending() || $self->{oReadSelect}->can_read($fRemaining))\n"
|
|
"{\n"
|
|
"\n"
|
|
"my $iReadSize = $self->parent()->read($tBufferRef, $iRemainingSize);\n"
|
|
"\n\n"
|
|
"if ($iReadSize == 0)\n"
|
|
"{\n"
|
|
"if ($bBlock)\n"
|
|
"{\n"
|
|
"$self->error(ERROR_FILE_READ, \"unable to read ${iRequestSize} byte(s) due to EOF from \" . $self->id());\n"
|
|
"}\n"
|
|
"else\n"
|
|
"{\n"
|
|
"return $iRequestSize - $iRemainingSize;\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"$iRemainingSize -= $iReadSize;\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"$fRemaining = $self->timeout() - (gettimeofday() - $fTimeStart);\n"
|
|
"};\n"
|
|
"\n\n"
|
|
"if ($iRemainingSize != 0 && $bBlock)\n"
|
|
"{\n"
|
|
"$self->error(\n"
|
|
"ERROR_FILE_READ, \"unable to read ${iRequestSize} byte(s) after \" . $self->timeout() . ' second(s) from ' . $self->id());\n"
|
|
"}\n"
|
|
"\n"
|
|
"return $iRequestSize - $iRemainingSize;\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub readLine\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"my $bIgnoreEOF = shift;\n"
|
|
"my $bError = shift;\n"
|
|
"\n\n"
|
|
"my $iLineFeedPos = index($self->{tBuffer}, \"\\n\", $self->{lBufferPos});\n"
|
|
"\n\n"
|
|
"if ($iLineFeedPos == -1)\n"
|
|
"{\n"
|
|
"my $fRemaining = $self->timeout();\n"
|
|
"my $fTimeStart = gettimeofday();\n"
|
|
"\n\n"
|
|
"do\n"
|
|
"{\n"
|
|
"\n"
|
|
"if ($self->{lBufferPos} != 0)\n"
|
|
"{\n"
|
|
"$self->{tBuffer} = substr($self->{tBuffer}, $self->{lBufferPos});\n"
|
|
"$self->{lBufferSize} = $self->{lBufferSize} - $self->{lBufferPos};\n"
|
|
"$self->{lBufferPos} = 0;\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"my $iBufferRead = 0;\n"
|
|
"\n"
|
|
"if ($self->pending() || $self->{oReadSelect}->can_read($fRemaining))\n"
|
|
"{\n"
|
|
"$iBufferRead = $self->parent()->read(\n"
|
|
"\\$self->{tBuffer},\n"
|
|
"$self->{lBufferSize} >= $self->bufferMax() ? $self->bufferMax() : $self->bufferMax() - $self->{lBufferSize});\n"
|
|
"\n\n"
|
|
"if ($iBufferRead == 0)\n"
|
|
"{\n"
|
|
"\n"
|
|
"if (defined($bIgnoreEOF) && $bIgnoreEOF)\n"
|
|
"{\n"
|
|
"return;\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"$self->error(ERROR_FILE_READ, 'unexpected EOF reading line from ' . $self->id());\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if ($iBufferRead > 0)\n"
|
|
"{\n"
|
|
"$self->{lBufferSize} += $iBufferRead;\n"
|
|
"\n"
|
|
"$iLineFeedPos = index($self->{tBuffer}, \"\\n\");\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if ($iLineFeedPos == -1)\n"
|
|
"{\n"
|
|
"$fRemaining = $self->timeout() - (gettimeofday() - $fTimeStart);\n"
|
|
"}\n"
|
|
"}\n"
|
|
"while ($iLineFeedPos == -1 && $fRemaining > 0);\n"
|
|
"\n\n"
|
|
"if ($iLineFeedPos == -1)\n"
|
|
"{\n"
|
|
"if (!defined($bError) || $bError)\n"
|
|
"{\n"
|
|
"$self->error(\n"
|
|
"ERROR_FILE_READ, 'unable to read line after ' . $self->timeout() . ' second(s) from ' . $self->id());\n"
|
|
"}\n"
|
|
"\n"
|
|
"return;\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"my $strLine = substr($self->{tBuffer}, $self->{lBufferPos}, $iLineFeedPos - $self->{lBufferPos});\n"
|
|
"$self->{lBufferPos} = $iLineFeedPos + 1;\n"
|
|
"\n"
|
|
"return $strLine;\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub writeLine\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"my $strBuffer = shift;\n"
|
|
"\n"
|
|
"$strBuffer .= \"\\n\";\n"
|
|
"return $self->parent()->write(\\$strBuffer);\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub timeout {shift->{iTimeout}};\n"
|
|
"sub bufferMax {shift->{lBufferMax}};\n"
|
|
"\n\n\n\n"
|
|
"sub handleReadSet\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"my $fhRead = shift;\n"
|
|
"\n"
|
|
"$self->parent()->handleReadSet($fhRead);\n"
|
|
"\n\n"
|
|
"$self->{oReadSelect} = IO::Select->new();\n"
|
|
"$self->{oReadSelect}->add($self->handleRead());\n"
|
|
"\n\n"
|
|
"$self->{bPending} = defined($fhRead) && $fhRead->can('pending') ? true : false;\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub pending\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n"
|
|
"return ($self->{bPending} && $self->handleRead()->pending() ? true : false);\n"
|
|
"}\n"
|
|
"\n"
|
|
"1;\n"
|
|
},
|
|
{
|
|
.name = "pgBackRest/Common/Io/Filter.pm",
|
|
.data =
|
|
"\n\n\n"
|
|
"package pgBackRest::Common::Io::Filter;\n"
|
|
"\n"
|
|
"use strict;\n"
|
|
"use warnings FATAL => qw(all);\n"
|
|
"use Carp qw(confess);\n"
|
|
"use English '-no_match_vars';\n"
|
|
"\n"
|
|
"use Exporter qw(import);\n"
|
|
"our @EXPORT = qw();\n"
|
|
"use Scalar::Util qw(blessed);\n"
|
|
"\n"
|
|
"use pgBackRest::Common::Log;\n"
|
|
"\n\n\n\n"
|
|
"sub new\n"
|
|
"{\n"
|
|
"my $class = shift;\n"
|
|
"\n\n"
|
|
"my $self = {};\n"
|
|
"bless $self, $class;\n"
|
|
"\n\n"
|
|
"(\n"
|
|
"my $strOperation,\n"
|
|
"$self->{oParent},\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->new', \\@_,\n"
|
|
"{name => 'oParent', trace => true},\n"
|
|
");\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'self', value => $self}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub bufferMax {shift->{oParent}->bufferMax()};\n"
|
|
"sub className {shift->{oParent}->className()};\n"
|
|
"sub close {shift->{oParent}->close()};\n"
|
|
"sub eof {shift->{oParent}->eof()};\n"
|
|
"sub eofSet {shift->{oParent}->eofSet(@_)};\n"
|
|
"sub error {shift->{oParent}->error(@_)};\n"
|
|
"sub id {shift->{oParent}->id()};\n"
|
|
"sub handleRead {shift->{oParent}->handleRead()};\n"
|
|
"sub handleReadSet {shift->{oParent}->handleReadSet(@_)};\n"
|
|
"sub handleWrite {shift->{oParent}->handleWrite()};\n"
|
|
"sub handleWriteSet {shift->{oParent}->handleWriteSet(@_)};\n"
|
|
"sub name {shift->{oParent}->name()};\n"
|
|
"sub read {shift->{oParent}->read(@_)};\n"
|
|
"sub readLine {shift->{oParent}->readLine(@_)};\n"
|
|
"sub result {shift->{oParent}->result(@_)};\n"
|
|
"sub resultAll {shift->{oParent}->resultAll()};\n"
|
|
"sub resultSet {shift->{oParent}->resultSet(@_)};\n"
|
|
"sub size {shift->{oParent}->size()};\n"
|
|
"sub timeout {shift->{oParent}->timeout()};\n"
|
|
"sub write {shift->{oParent}->write(@_)};\n"
|
|
"sub writeLine {shift->{oParent}->writeLine(@_)};\n"
|
|
"\n\n\n\n"
|
|
"sub parent {shift->{oParent}}\n"
|
|
"\n"
|
|
"1;\n"
|
|
},
|
|
{
|
|
.name = "pgBackRest/Common/Io/Handle.pm",
|
|
.data =
|
|
"\n\n\n"
|
|
"package pgBackRest::Common::Io::Handle;\n"
|
|
"use parent 'pgBackRest::Common::Io::Base';\n"
|
|
"\n"
|
|
"use strict;\n"
|
|
"use warnings FATAL => qw(all);\n"
|
|
"use Carp qw(confess);\n"
|
|
"use English '-no_match_vars';\n"
|
|
"\n"
|
|
"use Exporter qw(import);\n"
|
|
"our @EXPORT = qw();\n"
|
|
"\n"
|
|
"use pgBackRest::Common::Exception;\n"
|
|
"use pgBackRest::Common::Log;\n"
|
|
"\n\n\n\n"
|
|
"use constant COMMON_IO_HANDLE => __PACKAGE__;\n"
|
|
"push @EXPORT, qw(COMMON_IO_HANDLE);\n"
|
|
"\n\n\n\n"
|
|
"sub new\n"
|
|
"{\n"
|
|
"my $class = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$strId,\n"
|
|
"$fhRead,\n"
|
|
"$fhWrite,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->new', \\@_,\n"
|
|
"{name => 'strId', trace => true},\n"
|
|
"{name => 'fhRead', required => false, trace => true},\n"
|
|
"{name => 'fhWrite', required => false, trace => true},\n"
|
|
");\n"
|
|
"\n\n"
|
|
"my $self = $class->SUPER::new($strId);\n"
|
|
"bless $self, $class;\n"
|
|
"\n\n"
|
|
"$self->handleReadSet($fhRead) if defined($fhRead);\n"
|
|
"$self->handleWriteSet($fhWrite) if defined($fhWrite);\n"
|
|
"\n\n"
|
|
"$self->{lSize} = 0;\n"
|
|
"\n\n"
|
|
"$self->eofSet(false);\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'self', value => $self}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n\n\n\n"
|
|
"sub eof\n"
|
|
"{\n"
|
|
"return shift->{bEOF};\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub eofSet\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"my $bEOF = shift;\n"
|
|
"\n"
|
|
"$self->{bEOF} = $bEOF;\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub read\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"my $rtBuffer = shift;\n"
|
|
"my $iSize = shift;\n"
|
|
"\n\n"
|
|
"my $iActualSize;\n"
|
|
"\n"
|
|
"eval\n"
|
|
"{\n"
|
|
"$iActualSize = sysread($self->handleRead(), $$rtBuffer, $iSize, defined($$rtBuffer) ? length($$rtBuffer) : 0);\n"
|
|
"return true;\n"
|
|
"}\n"
|
|
"or do\n"
|
|
"{\n"
|
|
"$self->error(ERROR_FILE_READ, 'unable to read from ' . $self->id(), $EVAL_ERROR);\n"
|
|
"};\n"
|
|
"\n\n\n"
|
|
"defined($iActualSize)\n"
|
|
"or $self->error(ERROR_FILE_READ, 'unable to read from ' . $self->id(), $OS_ERROR);\n"
|
|
"\n\n"
|
|
"$self->{lSize} += $iActualSize;\n"
|
|
"\n\n"
|
|
"$self->eofSet($iActualSize == 0 ? true : false);\n"
|
|
"\n"
|
|
"return $iActualSize;\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub write\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"my $rtBuffer = shift;\n"
|
|
"\n\n"
|
|
"my $iActualSize;\n"
|
|
"\n"
|
|
"eval\n"
|
|
"{\n"
|
|
"$iActualSize = syswrite($self->handleWrite(), $$rtBuffer);\n"
|
|
"return true;\n"
|
|
"}\n"
|
|
"or do\n"
|
|
"{\n"
|
|
"$self->error(ERROR_FILE_WRITE, 'unable to write to ' . $self->id(), $EVAL_ERROR);\n"
|
|
"};\n"
|
|
"\n\n\n"
|
|
"defined($iActualSize)\n"
|
|
"or $self->error(ERROR_FILE_WRITE, 'unable to write to ' . $self->id(), $OS_ERROR);\n"
|
|
"\n\n"
|
|
"$self->{lSize} += $iActualSize;\n"
|
|
"\n"
|
|
"return $iActualSize;\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub close\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"if (defined($self->{lSize}))\n"
|
|
"{\n"
|
|
"$self->resultSet(COMMON_IO_HANDLE, $self->{lSize});\n"
|
|
"undef($self->{lSize});\n"
|
|
"}\n"
|
|
"\n"
|
|
"return true;\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub handleRead {return shift->{fhHandleRead}}\n"
|
|
"sub handleReadSet {my $self = shift; $self->{fhHandleRead} = shift}\n"
|
|
"sub handleWrite {return shift->{fhHandleWrite}}\n"
|
|
"sub handleWriteSet {my $self = shift; $self->{fhHandleWrite} = shift}\n"
|
|
"sub size {shift->{lSize}}\n"
|
|
"\n"
|
|
"1;\n"
|
|
},
|
|
{
|
|
.name = "pgBackRest/Common/Io/Process.pm",
|
|
.data =
|
|
"\n\n\n"
|
|
"package pgBackRest::Common::Io::Process;\n"
|
|
"use parent 'pgBackRest::Common::Io::Filter';\n"
|
|
"\n"
|
|
"use strict;\n"
|
|
"use warnings FATAL => qw(all);\n"
|
|
"use Carp qw(confess);\n"
|
|
"use English '-no_match_vars';\n"
|
|
"\n"
|
|
"use Exporter qw(import);\n"
|
|
"our @EXPORT = qw();\n"
|
|
"use IPC::Open3 qw(open3);\n"
|
|
"use POSIX qw(:sys_wait_h);\n"
|
|
"use Symbol 'gensym';\n"
|
|
"\n"
|
|
"use pgBackRest::Common::Exception;\n"
|
|
"use pgBackRest::Common::Io::Buffered;\n"
|
|
"use pgBackRest::Common::Log;\n"
|
|
"use pgBackRest::Common::Wait;\n"
|
|
"\n\n\n\n"
|
|
"use constant COMMON_IO_PROCESS => __PACKAGE__;\n"
|
|
"push @EXPORT, qw(COMMON_IO_PROCESS);\n"
|
|
"\n\n\n\n"
|
|
"use constant IO_ERROR_TIMEOUT => 5;\n"
|
|
"\n\n\n\n"
|
|
"sub new\n"
|
|
"{\n"
|
|
"my $class = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$oParent,\n"
|
|
"$strCommand,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->new', \\@_,\n"
|
|
"{name => 'oParent', trace => true},\n"
|
|
"{name => 'strCommand', trace => true},\n"
|
|
");\n"
|
|
"\n\n"
|
|
"my $self = $class->SUPER::new($oParent);\n"
|
|
"bless $self, $class;\n"
|
|
"\n\n"
|
|
"my ($iProcessId, $fhRead, $fhWrite, $fhReadError);\n"
|
|
"$fhReadError = gensym;\n"
|
|
"\n"
|
|
"$iProcessId = IPC::Open3::open3($fhWrite, $fhRead, $fhReadError, $strCommand);\n"
|
|
"\n\n"
|
|
"$self->handleReadSet($fhRead);\n"
|
|
"$self->handleWriteSet($fhWrite);\n"
|
|
"\n\n"
|
|
"$self->{iProcessId} = $iProcessId;\n"
|
|
"$self->{fhReadError} = $fhReadError;\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'self', value => $self}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub error\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"my $iCode = shift;\n"
|
|
"my $strMessage = shift;\n"
|
|
"my $strDetail = shift;\n"
|
|
"my $bClose = shift;\n"
|
|
"\n"
|
|
"if (defined($self->{iProcessId}))\n"
|
|
"{\n"
|
|
"my $oWait = waitInit(defined($iCode) ? IO_ERROR_TIMEOUT : 0);\n"
|
|
"\n"
|
|
"do\n"
|
|
"{\n"
|
|
"\n"
|
|
"my $iResult = waitpid($self->{iProcessId}, $bClose ? 0 : WNOHANG);\n"
|
|
"\n\n"
|
|
"if ($iResult != 0)\n"
|
|
"{\n"
|
|
"\n"
|
|
"my $iExitStatus = $iResult == -1 ? 255 : ${^CHILD_ERROR_NATIVE} >> 8;\n"
|
|
"\n\n"
|
|
"my $strError;\n"
|
|
"my $oIoError = new pgBackRest::Common::Io::Buffered(\n"
|
|
"new pgBackRest::Common::Io::Handle($self->id(), $self->{fhReadError}), 5, $self->bufferMax());\n"
|
|
"\n"
|
|
"while (defined(my $strLine = $oIoError->readLine(true, false)))\n"
|
|
"{\n"
|
|
"$strError .= (defined($strError) ? \"\\n\" : '') . $strLine;\n"
|
|
"}\n"
|
|
"\n"
|
|
"delete($self->{iProcessId});\n"
|
|
"\n"
|
|
"if (!$bClose || $iExitStatus != 0 || defined($strError))\n"
|
|
"{\n"
|
|
"my $iErrorCode =\n"
|
|
"$iExitStatus >= ERROR_MINIMUM && $iExitStatus <= ERROR_MAXIMUM ? $iExitStatus : ERROR_FILE_READ;\n"
|
|
"\n"
|
|
"logErrorResult(\n"
|
|
"$iErrorCode, $self->id() . ' terminated unexpectedly' .\n"
|
|
"($iExitStatus != 255 ? sprintf(' [%03d]', $iExitStatus) : ''),\n"
|
|
"$strError);\n"
|
|
"}\n"
|
|
"}\n"
|
|
"}\n"
|
|
"while (waitMore($oWait));\n"
|
|
"\n"
|
|
"if (defined($iCode))\n"
|
|
"{\n"
|
|
"$self->parent()->error($iCode, $strMessage, $strDetail);\n"
|
|
"}\n"
|
|
"}\n"
|
|
"else\n"
|
|
"{\n"
|
|
"confess &log(ASSERT, 'cannot call error() after process has been closed');\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub processId\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n"
|
|
"return $self->{iProcessId};\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub writeLine\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"my $strBuffer = shift;\n"
|
|
"\n\n\n"
|
|
"$self->error();\n"
|
|
"\n"
|
|
"return $self->parent()->writeLine($strBuffer);\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub close\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n"
|
|
"if (defined($self->{iProcessId}))\n"
|
|
"{\n"
|
|
"$self->error(undef, undef, undef, true);\n"
|
|
"\n\n"
|
|
"$self->parent()->close();\n"
|
|
"}\n"
|
|
"\n"
|
|
"return true;\n"
|
|
"}\n"
|
|
"\n"
|
|
"1;\n"
|
|
},
|
|
{
|
|
.name = "pgBackRest/Common/Lock.pm",
|
|
.data =
|
|
"\n\n\n"
|
|
"package pgBackRest::Common::Lock;\n"
|
|
"\n"
|
|
"use strict;\n"
|
|
"use warnings FATAL => qw(all);\n"
|
|
"use Carp qw(confess);\n"
|
|
"\n"
|
|
"use Exporter qw(import);\n"
|
|
"our @EXPORT = qw();\n"
|
|
"use Fcntl qw(:DEFAULT :flock);\n"
|
|
"use File::Basename qw(dirname);\n"
|
|
"\n"
|
|
"use pgBackRest::Common::Exception;\n"
|
|
"use pgBackRest::Common::Log;\n"
|
|
"use pgBackRest::Common::String;\n"
|
|
"use pgBackRest::Common::Wait;\n"
|
|
"use pgBackRest::Config::Config;\n"
|
|
"use pgBackRest::Storage::Helper;\n"
|
|
"\n\n\n\n\n\n"
|
|
"sub lockPathCreate\n"
|
|
"{\n"
|
|
"storageLocal()->pathCreate(cfgOption(CFGOPT_LOCK_PATH), {strMode => '770', bIgnoreExists => true, bCreateParent => true});\n"
|
|
"}\n"
|
|
"\n\n\n\n\n\n"
|
|
"sub lockStopFileName\n"
|
|
"{\n"
|
|
"my $strStanza = shift;\n"
|
|
"\n"
|
|
"return cfgOption(CFGOPT_LOCK_PATH) . (defined($strStanza) ? \"/${strStanza}\" : '/all') . '.stop';\n"
|
|
"}\n"
|
|
"\n\n\n\n\n\n\n"
|
|
"sub lockStop\n"
|
|
"{\n"
|
|
"\n"
|
|
"lockPathCreate();\n"
|
|
"\n\n"
|
|
"my $strStopFile = lockStopFileName(cfgOption(CFGOPT_STANZA, false));\n"
|
|
"\n\n"
|
|
"if (-e $strStopFile)\n"
|
|
"{\n"
|
|
"&log(WARN, 'stop file already exists' .\n"
|
|
"(cfgOptionTest(CFGOPT_STANZA) ? ' for stanza ' . cfgOption(CFGOPT_STANZA) : ' for all stanzas'));\n"
|
|
"return false;\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"sysopen(my $hStopHandle, $strStopFile, O_WRONLY | O_CREAT, oct(640))\n"
|
|
"or confess &log(ERROR, \"unable to open stop file ${strStopFile}\", ERROR_FILE_OPEN);\n"
|
|
"close($hStopHandle);\n"
|
|
"\n\n"
|
|
"if (cfgOption(CFGOPT_FORCE))\n"
|
|
"{\n"
|
|
"my $strLockPath = cfgOption(CFGOPT_LOCK_PATH);\n"
|
|
"\n"
|
|
"opendir(my $hPath, $strLockPath)\n"
|
|
"or confess &log(ERROR, \"unable to open lock path ${strLockPath}\", ERROR_PATH_OPEN);\n"
|
|
"\n"
|
|
"my @stryFileList = grep(!/^(\\.)|(\\.\\.)$/i, readdir($hPath));\n"
|
|
"\n\n"
|
|
"foreach my $strFile (sort(@stryFileList))\n"
|
|
"{\n"
|
|
"my $hLockHandle;\n"
|
|
"my $strLockFile = \"${strLockPath}/${strFile}\";\n"
|
|
"\n\n"
|
|
"next if ($strFile =~ /\\.stop$/);\n"
|
|
"\n\n"
|
|
"if (!sysopen($hLockHandle, $strLockFile, O_RDONLY))\n"
|
|
"{\n"
|
|
"&log(WARN, \"unable to open lock file ${strLockFile}\");\n"
|
|
"next;\n"
|
|
"}\n"
|
|
"\n\n\n"
|
|
"if (flock($hLockHandle, LOCK_EX | LOCK_NB))\n"
|
|
"{\n"
|
|
"unlink($strLockFile);\n"
|
|
"close($hLockHandle);\n"
|
|
"next;\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"my $iProcessId = trim(readline($hLockHandle));\n"
|
|
"\n\n"
|
|
"if (defined($iProcessId))\n"
|
|
"{\n"
|
|
"if (!kill('TERM', $iProcessId))\n"
|
|
"{\n"
|
|
"&log(WARN, \"unable to send term signal to process ${iProcessId}\");\n"
|
|
"}\n"
|
|
"\n"
|
|
"&log(INFO, \"sent term signal to process ${iProcessId}\");\n"
|
|
"}\n"
|
|
"\n"
|
|
"{\n"
|
|
"unlink($strLockFile);\n"
|
|
"close($hLockHandle);\n"
|
|
"next;\n"
|
|
"}\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n"
|
|
"return true;\n"
|
|
"}\n"
|
|
"\n"
|
|
"push @EXPORT, qw(lockStop);\n"
|
|
"\n\n\n\n\n\n"
|
|
"sub lockStopTest\n"
|
|
"{\n"
|
|
"\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$bStanzaStopRequired,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '::lockStopTest', \\@_,\n"
|
|
"{name => 'bStanzaStopRequired', optional => true, default => false}\n"
|
|
");\n"
|
|
"\n"
|
|
"my $bStopExists = false;\n"
|
|
"\n\n"
|
|
"if (cfgOptionTest(CFGOPT_STANZA))\n"
|
|
"{\n"
|
|
"\n"
|
|
"my $strStopFile = lockStopFileName(cfgOption(CFGOPT_STANZA));\n"
|
|
"\n"
|
|
"if (-e $strStopFile)\n"
|
|
"{\n"
|
|
"\n"
|
|
"if ($bStanzaStopRequired)\n"
|
|
"{\n"
|
|
"$bStopExists = true;\n"
|
|
"}\n"
|
|
"else\n"
|
|
"{\n"
|
|
"confess &log(ERROR, 'stop file exists for stanza ' . cfgOption(CFGOPT_STANZA), ERROR_STOP);\n"
|
|
"}\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if (!$bStanzaStopRequired)\n"
|
|
"{\n"
|
|
"\n"
|
|
"my $strStopFile = lockStopFileName();\n"
|
|
"\n"
|
|
"if (-e $strStopFile)\n"
|
|
"{\n"
|
|
"confess &log(ERROR, 'stop file exists for all stanzas', ERROR_STOP);\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'bStopExists', value => $bStopExists}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n"
|
|
"push @EXPORT, qw(lockStopTest);\n"
|
|
"\n\n\n\n\n\n"
|
|
"sub lockStart\n"
|
|
"{\n"
|
|
"\n"
|
|
"my $strStopFile = lockStopFileName(cfgOption(CFGOPT_STANZA, false));\n"
|
|
"\n\n"
|
|
"if (!-e $strStopFile)\n"
|
|
"{\n"
|
|
"&log(WARN, 'stop file does not exist' . (cfgOptionTest(CFGOPT_STANZA) ? ' for stanza ' . cfgOption(CFGOPT_STANZA) : ''));\n"
|
|
"return false;\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"unlink($strStopFile)\n"
|
|
"or confess &log(ERROR, \"unable to remove ${strStopFile}: $!\");\n"
|
|
"\n"
|
|
"return true;\n"
|
|
"}\n"
|
|
"\n"
|
|
"push @EXPORT, qw(lockStart);\n"
|
|
"\n"
|
|
"1;\n"
|
|
},
|
|
{
|
|
.name = "pgBackRest/Common/Log.pm",
|
|
.data =
|
|
"\n\n\n"
|
|
"package pgBackRest::Common::Log;\n"
|
|
"\n"
|
|
"use strict;\n"
|
|
"use warnings FATAL => qw(all);\n"
|
|
"use Carp qw(confess longmess);\n"
|
|
"use English '-no_match_vars';\n"
|
|
"\n"
|
|
"use Exporter qw(import);\n"
|
|
"our @EXPORT = qw();\n"
|
|
"use Fcntl qw(:DEFAULT :flock);\n"
|
|
"use File::Basename qw(dirname);\n"
|
|
"use Scalar::Util qw(blessed reftype);\n"
|
|
"use Time::HiRes qw(gettimeofday usleep);\n"
|
|
"\n"
|
|
"use pgBackRest::Common::Exception;\n"
|
|
"use pgBackRest::Common::String;\n"
|
|
"\n\n\n\n"
|
|
"use constant true => 1;\n"
|
|
"push @EXPORT, qw(true);\n"
|
|
"use constant false => 0;\n"
|
|
"push @EXPORT, qw(false);\n"
|
|
"\n\n\n\n"
|
|
"use constant TRACE => 'TRACE';\n"
|
|
"push @EXPORT, qw(TRACE);\n"
|
|
"use constant DEBUG => 'DEBUG';\n"
|
|
"push @EXPORT, qw(DEBUG);\n"
|
|
"use constant DETAIL => 'DETAIL';\n"
|
|
"push @EXPORT, qw(DETAIL);\n"
|
|
"use constant INFO => 'INFO';\n"
|
|
"push @EXPORT, qw(INFO);\n"
|
|
"use constant WARN => 'WARN';\n"
|
|
"push @EXPORT, qw(WARN);\n"
|
|
"use constant PROTOCOL => 'PROTOCOL';\n"
|
|
"push @EXPORT, qw(PROTOCOL);\n"
|
|
"use constant ERROR => 'ERROR';\n"
|
|
"push @EXPORT, qw(ERROR);\n"
|
|
"use constant ASSERT => 'ASSERT';\n"
|
|
"push @EXPORT, qw(ASSERT);\n"
|
|
"use constant OFF => 'OFF';\n"
|
|
"push @EXPORT, qw(OFF);\n"
|
|
"\n\n\n\n"
|
|
"my %oLogLevelRank;\n"
|
|
"\n"
|
|
"$oLogLevelRank{TRACE}{rank} = 8;\n"
|
|
"$oLogLevelRank{DEBUG}{rank} = 7;\n"
|
|
"$oLogLevelRank{DETAIL}{rank} = 6;\n"
|
|
"$oLogLevelRank{INFO}{rank} = 5;\n"
|
|
"$oLogLevelRank{WARN}{rank} = 4;\n"
|
|
"$oLogLevelRank{PROTOCOL}{rank} = 3;\n"
|
|
"$oLogLevelRank{ERROR}{rank} = 2;\n"
|
|
"$oLogLevelRank{ASSERT}{rank} = 1;\n"
|
|
"$oLogLevelRank{OFF}{rank} = 0;\n"
|
|
"\n\n\n\n"
|
|
"my $hLogFile = undef;\n"
|
|
"my $strLogFileCache = undef;\n"
|
|
"\n"
|
|
"my $strLogLevelFile = OFF;\n"
|
|
"my $strLogLevelConsole = OFF;\n"
|
|
"my $strLogLevelStdErr = WARN;\n"
|
|
"my $bLogTimestamp = true;\n"
|
|
"\n\n"
|
|
"my $iLogProcessSize = 2;\n"
|
|
"\n\n"
|
|
"my $bLogFileExists;\n"
|
|
"my $bLogFileFirst;\n"
|
|
"\n\n"
|
|
"my $bLogDisable = 0;\n"
|
|
"\n\n"
|
|
"my $bLogWarnOnError = 0;\n"
|
|
"\n\n"
|
|
"my $oErrorLast;\n"
|
|
"\n\n"
|
|
"my $bTest = false;\n"
|
|
"my $fTestDelay;\n"
|
|
"my $oTestPoint;\n"
|
|
"\n\n\n\n"
|
|
"use constant TEST => 'TEST';\n"
|
|
"push @EXPORT, qw(TEST);\n"
|
|
"use constant TEST_ENCLOSE => 'PgBaCkReStTeSt';\n"
|
|
"push @EXPORT, qw(TEST_ENCLOSE);\n"
|
|
"\n"
|
|
"use constant TEST_MANIFEST_BUILD => 'MANIFEST-BUILD';\n"
|
|
"push @EXPORT, qw(TEST_MANIFEST_BUILD);\n"
|
|
"use constant TEST_BACKUP_RESUME => 'BACKUP-RESUME';\n"
|
|
"push @EXPORT, qw(TEST_BACKUP_RESUME);\n"
|
|
"use constant TEST_BACKUP_NORESUME => 'BACKUP-NORESUME';\n"
|
|
"push @EXPORT, qw(TEST_BACKUP_NORESUME);\n"
|
|
"use constant TEST_BACKUP_START => 'BACKUP-START';\n"
|
|
"push @EXPORT, qw(TEST_BACKUP_START);\n"
|
|
"use constant TEST_BACKUP_STOP => 'BACKUP-STOP';\n"
|
|
"push @EXPORT, qw(TEST_BACKUP_STOP);\n"
|
|
"use constant TEST_KEEP_ALIVE => 'KEEP_ALIVE';\n"
|
|
"push @EXPORT, qw(TEST_KEEP_ALIVE);\n"
|
|
"use constant TEST_ARCHIVE_PUSH_ASYNC_START => 'ARCHIVE-PUSH-ASYNC-START';\n"
|
|
"push @EXPORT, qw(TEST_ARCHIVE_PUSH_ASYNC_START);\n"
|
|
"\n\n\n\n"
|
|
"sub logFileSet\n"
|
|
"{\n"
|
|
"my $oStorage = shift;\n"
|
|
"my $strFile = shift;\n"
|
|
"my $bLogFileFirstParam = shift;\n"
|
|
"\n\n"
|
|
"if ($strLogLevelFile ne OFF)\n"
|
|
"{\n"
|
|
"$oStorage->pathCreate(dirname($strFile), {strMode => '0770', bIgnoreExists => true, bCreateParent => true});\n"
|
|
"\n"
|
|
"$strFile .= '.log';\n"
|
|
"$bLogFileExists = -e $strFile ? true : false;\n"
|
|
"$bLogFileFirst = defined($bLogFileFirstParam) ? $bLogFileFirstParam : false;\n"
|
|
"\n"
|
|
"if (!sysopen($hLogFile, $strFile, O_WRONLY | O_CREAT | O_APPEND, oct('0660')))\n"
|
|
"{\n"
|
|
"logErrorResult(ERROR_FILE_OPEN, \"unable to open log file '${strFile}'\", $OS_ERROR);\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if (defined($strLogFileCache))\n"
|
|
"{\n"
|
|
"logBanner();\n"
|
|
"syswrite($hLogFile, $strLogFileCache);\n"
|
|
"undef($strLogFileCache);\n"
|
|
"}\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n"
|
|
"push @EXPORT, qw(logFileSet);\n"
|
|
"\n\n\n\n\n\n"
|
|
"sub logBanner\n"
|
|
"{\n"
|
|
"if ($bLogFileFirst)\n"
|
|
"{\n"
|
|
"if ($bLogFileExists)\n"
|
|
"{\n"
|
|
"syswrite($hLogFile, \"\\n\");\n"
|
|
"}\n"
|
|
"\n"
|
|
"syswrite($hLogFile, \"-------------------PROCESS START-------------------\\n\");\n"
|
|
"}\n"
|
|
"\n"
|
|
"$bLogFileFirst = false;\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub logLevelSet\n"
|
|
"{\n"
|
|
"my $strLevelFileParam = shift;\n"
|
|
"my $strLevelConsoleParam = shift;\n"
|
|
"my $strLevelStdErrParam = shift;\n"
|
|
"my $bLogTimestampParam = shift;\n"
|
|
"my $iLogProcessMax = shift;\n"
|
|
"\n"
|
|
"if (defined($strLevelFileParam))\n"
|
|
"{\n"
|
|
"if (!defined($oLogLevelRank{uc($strLevelFileParam)}{rank}))\n"
|
|
"{\n"
|
|
"confess &log(ERROR, \"file log level ${strLevelFileParam} does not exist\");\n"
|
|
"}\n"
|
|
"\n"
|
|
"$strLogLevelFile = uc($strLevelFileParam);\n"
|
|
"}\n"
|
|
"\n"
|
|
"if (defined($strLevelConsoleParam))\n"
|
|
"{\n"
|
|
"if (!defined($oLogLevelRank{uc($strLevelConsoleParam)}{rank}))\n"
|
|
"{\n"
|
|
"confess &log(ERROR, \"console log level ${strLevelConsoleParam} does not exist\");\n"
|
|
"}\n"
|
|
"\n"
|
|
"$strLogLevelConsole = uc($strLevelConsoleParam);\n"
|
|
"}\n"
|
|
"\n"
|
|
"if (defined($strLevelStdErrParam))\n"
|
|
"{\n"
|
|
"if (!defined($oLogLevelRank{uc($strLevelStdErrParam)}{rank}))\n"
|
|
"{\n"
|
|
"confess &log(ERROR, \"stdout log level ${strLevelStdErrParam} does not exist\");\n"
|
|
"}\n"
|
|
"\n"
|
|
"$strLogLevelStdErr = uc($strLevelStdErrParam);\n"
|
|
"}\n"
|
|
"\n"
|
|
"if (defined($bLogTimestampParam))\n"
|
|
"{\n"
|
|
"$bLogTimestamp = $bLogTimestampParam;\n"
|
|
"}\n"
|
|
"\n"
|
|
"if (defined($iLogProcessMax))\n"
|
|
"{\n"
|
|
"$iLogProcessSize = $iLogProcessMax > 99 ? 3 : 2;\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n"
|
|
"push @EXPORT, qw(logLevelSet);\n"
|
|
"\n\n\n\n"
|
|
"sub logDisable\n"
|
|
"{\n"
|
|
"$bLogDisable++;\n"
|
|
"}\n"
|
|
"\n"
|
|
"push @EXPORT, qw(logDisable);\n"
|
|
"\n\n\n\n"
|
|
"sub logEnable\n"
|
|
"{\n"
|
|
"$bLogDisable--;\n"
|
|
"}\n"
|
|
"\n"
|
|
"push @EXPORT, qw(logEnable);\n"
|
|
"\n\n\n\n"
|
|
"sub logWarnOnErrorDisable\n"
|
|
"{\n"
|
|
"$bLogWarnOnError--;\n"
|
|
"}\n"
|
|
"\n"
|
|
"push @EXPORT, qw(logWarnOnErrorDisable);\n"
|
|
"\n\n\n\n"
|
|
"sub logWarnOnErrorEnable\n"
|
|
"{\n"
|
|
"$bLogWarnOnError++;\n"
|
|
"}\n"
|
|
"\n"
|
|
"push @EXPORT, qw(logWarnOnErrorEnable);\n"
|
|
"\n\n\n\n\n\n"
|
|
"use constant DEBUG_PARAM => '()';\n"
|
|
"\n"
|
|
"sub logDebugParam\n"
|
|
"{\n"
|
|
"my $strFunction = shift;\n"
|
|
"my $oyParamRef = shift;\n"
|
|
"\n"
|
|
"return logDebugProcess($strFunction, DEBUG_PARAM, undef, $oyParamRef, @_);\n"
|
|
"}\n"
|
|
"\n"
|
|
"push @EXPORT, qw(logDebugParam);\n"
|
|
"\n\n\n\n\n\n"
|
|
"use constant DEBUG_RETURN => '=>';\n"
|
|
"\n"
|
|
"sub logDebugReturn\n"
|
|
"{\n"
|
|
"my $strFunction = shift;\n"
|
|
"\n"
|
|
"return logDebugProcess($strFunction, DEBUG_RETURN, undef, undef, @_);\n"
|
|
"}\n"
|
|
"\n"
|
|
"push @EXPORT, qw(logDebugReturn);\n"
|
|
"\n\n\n\n\n\n"
|
|
"use constant DEBUG_MISC => '';\n"
|
|
"\n"
|
|
"sub logDebugMisc\n"
|
|
"{\n"
|
|
"my $strFunction = shift;\n"
|
|
"my $strDetail = shift;\n"
|
|
"\n"
|
|
"return logDebugProcess($strFunction, DEBUG_MISC, $strDetail, undef, @_);\n"
|
|
"}\n"
|
|
"\n"
|
|
"push @EXPORT, qw(logDebugMisc);\n"
|
|
"\n\n\n\n"
|
|
"sub logDebugProcess\n"
|
|
"{\n"
|
|
"my $strFunction = shift;\n"
|
|
"my $strType = shift;\n"
|
|
"my $strDetail = shift;\n"
|
|
"my $oyParamRef = shift;\n"
|
|
"\n"
|
|
"my $iIndex = 0;\n"
|
|
"my $oParamHash = {};\n"
|
|
"my @oyResult;\n"
|
|
"my $bLogTrace = true;\n"
|
|
"\n"
|
|
"if ($strType eq DEBUG_PARAM)\n"
|
|
"{\n"
|
|
"push @oyResult, $strFunction;\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"my $oParam = shift;\n"
|
|
"my $bOptionalBlock = false;\n"
|
|
"\n\n"
|
|
"$strFunction =~ s/^pgBackRest[^\\:]*\\:\\://;\n"
|
|
"\n"
|
|
"while (defined($oParam))\n"
|
|
"{\n"
|
|
"my $strParamName = $$oParam{name};\n"
|
|
"my $bParamOptional = defined($oParam->{optional}) && $oParam->{optional};\n"
|
|
"my $bParamRequired = !defined($oParam->{required}) || $oParam->{required};\n"
|
|
"my $oValue;\n"
|
|
"\n\n"
|
|
"$oParamHash->{$strParamName}{redact} = $oParam->{redact} ? true : false;\n"
|
|
"\n\n"
|
|
"if ($bParamOptional)\n"
|
|
"{\n"
|
|
"if (defined($oParam->{required}))\n"
|
|
"{\n"
|
|
"confess &log(ASSERT, \"cannot define 'required' for optional parameter '${strParamName}'\");\n"
|
|
"}\n"
|
|
"\n"
|
|
"$bParamRequired = false;\n"
|
|
"$bOptionalBlock = true;\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if ($bParamOptional != $bOptionalBlock)\n"
|
|
"{\n"
|
|
"confess &log(ASSERT, \"non-optional parameter '${strParamName}' invalid after optional parameters\");\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if ($strType eq DEBUG_PARAM)\n"
|
|
"{\n"
|
|
"if ($bParamOptional)\n"
|
|
"{\n"
|
|
"$oValue = $$oyParamRef[$iIndex]->{$strParamName};\n"
|
|
"}\n"
|
|
"else\n"
|
|
"{\n"
|
|
"$oValue = $$oyParamRef[$iIndex];\n"
|
|
"}\n"
|
|
"\n"
|
|
"if (defined($oValue))\n"
|
|
"{\n"
|
|
"push(@oyResult, $oValue);\n"
|
|
"}\n"
|
|
"else\n"
|
|
"{\n"
|
|
"push(@oyResult, $${oParam}{default});\n"
|
|
"$$oParamHash{$strParamName}{default} = true;\n"
|
|
"}\n"
|
|
"\n"
|
|
"$oValue = $oyResult[-1];\n"
|
|
"\n"
|
|
"if (!defined($oValue) && $bParamRequired)\n"
|
|
"{\n"
|
|
"confess &log(ASSERT, \"${strParamName} is required in ${strFunction}\");\n"
|
|
"}\n"
|
|
"}\n"
|
|
"else\n"
|
|
"{\n"
|
|
"if (ref($$oParam{value}) eq 'ARRAY')\n"
|
|
"{\n"
|
|
"if (defined($$oParam{ref}) && $$oParam{ref})\n"
|
|
"{\n"
|
|
"push(@oyResult, $$oParam{value});\n"
|
|
"}\n"
|
|
"else\n"
|
|
"{\n"
|
|
"push(@oyResult, @{$$oParam{value}});\n"
|
|
"}\n"
|
|
"}\n"
|
|
"else\n"
|
|
"{\n"
|
|
"push(@oyResult, $$oParam{value});\n"
|
|
"}\n"
|
|
"\n"
|
|
"$oValue = $$oParam{value};\n"
|
|
"}\n"
|
|
"\n"
|
|
"if (!defined($$oParam{log}) || $$oParam{log})\n"
|
|
"{\n"
|
|
"\n\n"
|
|
"if (ref($oValue) eq 'HASH' && !blessed($oValue))\n"
|
|
"{\n"
|
|
"$$oParamHash{$strParamName}{value} = '[hash]';\n"
|
|
"}\n"
|
|
"\n"
|
|
"else\n"
|
|
"{\n"
|
|
"$$oParamHash{$strParamName}{value} = $oValue;\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if (!($strParamName eq 'self') &&\n"
|
|
"(!defined($$oParam{trace}) || !$$oParam{trace}))\n"
|
|
"{\n"
|
|
"$bLogTrace = false;\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"$oParam = shift;\n"
|
|
"\n"
|
|
"if (!$bParamOptional)\n"
|
|
"{\n"
|
|
"$iIndex++;\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n"
|
|
"if (defined($strDetail) && $iIndex == 0)\n"
|
|
"{\n"
|
|
"$bLogTrace = false;\n"
|
|
"}\n"
|
|
"\n"
|
|
"logDebugOut($strFunction, $strType, $strDetail, $oParamHash, $bLogTrace ? TRACE : DEBUG);\n"
|
|
"\n\n"
|
|
"if (@oyResult == 1)\n"
|
|
"{\n"
|
|
"return $oyResult[0];\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"return @oyResult;\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub logDebugBuild\n"
|
|
"{\n"
|
|
"my $strValue = shift;\n"
|
|
"\n"
|
|
"my $rResult;\n"
|
|
"\n\n"
|
|
"if (!defined($strValue))\n"
|
|
"{\n"
|
|
"$rResult = \\'[undef]';\n"
|
|
"}\n"
|
|
"\n"
|
|
"elsif (!ref($strValue))\n"
|
|
"{\n"
|
|
"$rResult = \\$strValue;\n"
|
|
"}\n"
|
|
"\n"
|
|
"elsif (ref($strValue) eq 'HASH')\n"
|
|
"{\n"
|
|
"my $strValueHash;\n"
|
|
"\n"
|
|
"for my $strSubValue (sort(keys(%{$strValue})))\n"
|
|
"{\n"
|
|
"$strValueHash .=\n"
|
|
"(defined($strValueHash) ? ', ' : '{') . \"${strSubValue} => \" . ${logDebugBuild($strValue->{$strSubValue})};\n"
|
|
"}\n"
|
|
"\n"
|
|
"$rResult = \\(defined($strValueHash) ? $strValueHash . '}' : '{}');\n"
|
|
"}\n"
|
|
"\n"
|
|
"elsif (ref($strValue) eq 'ARRAY')\n"
|
|
"{\n"
|
|
"my $strValueArray;\n"
|
|
"\n"
|
|
"for my $strSubValue (@{$strValue})\n"
|
|
"{\n"
|
|
"$strValueArray .= (defined($strValueArray) ? ', ' : '(') . ${logDebugBuild($strSubValue)};\n"
|
|
"}\n"
|
|
"\n"
|
|
"$rResult = \\(defined($strValueArray) ? $strValueArray . ')' : '()');\n"
|
|
"}\n"
|
|
"\n"
|
|
"else\n"
|
|
"{\n"
|
|
"$rResult = \\('[object]');\n"
|
|
"}\n"
|
|
"\n"
|
|
"return $rResult;\n"
|
|
"}\n"
|
|
"\n"
|
|
"push @EXPORT, qw(logDebugBuild);\n"
|
|
"\n\n\n\n"
|
|
"use constant DEBUG_STRING_MAX_LEN => 1024;\n"
|
|
"\n"
|
|
"sub logDebugOut\n"
|
|
"{\n"
|
|
"my $strFunction = shift;\n"
|
|
"my $strType = shift;\n"
|
|
"my $strMessage = shift;\n"
|
|
"my $oParamHash = shift;\n"
|
|
"my $strLevel = shift;\n"
|
|
"\n"
|
|
"$strLevel = defined($strLevel) ? $strLevel : DEBUG;\n"
|
|
"\n"
|
|
"if ($oLogLevelRank{$strLevel}{rank} <= $oLogLevelRank{$strLogLevelConsole}{rank} ||\n"
|
|
"$oLogLevelRank{$strLevel}{rank} <= $oLogLevelRank{$strLogLevelFile}{rank} ||\n"
|
|
"$oLogLevelRank{$strLevel}{rank} <= $oLogLevelRank{$strLogLevelStdErr}{rank})\n"
|
|
"{\n"
|
|
"if (defined($oParamHash))\n"
|
|
"{\n"
|
|
"my $strParamSet;\n"
|
|
"\n"
|
|
"foreach my $strParam (sort(keys(%$oParamHash)))\n"
|
|
"{\n"
|
|
"if (defined($strParamSet))\n"
|
|
"{\n"
|
|
"$strParamSet .= ', ';\n"
|
|
"}\n"
|
|
"\n"
|
|
"my $strValueRef = defined($oParamHash->{$strParam}{value}) ? logDebugBuild($oParamHash->{$strParam}{value}) : undef;\n"
|
|
"my $bDefault =\n"
|
|
"defined($$strValueRef) && defined($$oParamHash{$strParam}{default}) ? $$oParamHash{$strParam}{default} : false;\n"
|
|
"\n"
|
|
"$strParamSet .=\n"
|
|
"\"${strParam} = \" .\n"
|
|
"($oParamHash->{$strParam}{redact} && defined($$strValueRef) ? '<redacted>' :\n"
|
|
"($bDefault ? '<' : '') .\n"
|
|
"(defined($$strValueRef) ?\n"
|
|
"($strParam =~ /^(b|is)/ ? ($$strValueRef ? 'true' : 'false'):\n"
|
|
"(length($$strValueRef) > DEBUG_STRING_MAX_LEN ?\n"
|
|
"substr($$strValueRef, 0, DEBUG_STRING_MAX_LEN) . ' ... <truncated>':\n"
|
|
"$$strValueRef)) : '[undef]') .\n"
|
|
"($bDefault ? '>' : ''));\n"
|
|
"}\n"
|
|
"\n"
|
|
"if (defined($strMessage))\n"
|
|
"{\n"
|
|
"$strMessage = $strMessage . (defined($strParamSet) ? \": ${strParamSet}\" : '');\n"
|
|
"}\n"
|
|
"else\n"
|
|
"{\n"
|
|
"$strMessage = $strParamSet;\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n"
|
|
"&log($strLevel, \"${strFunction}${strType}\" . (defined($strMessage) ? \": $strMessage\" : ''));\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub logException\n"
|
|
"{\n"
|
|
"my $oException = shift;\n"
|
|
"\n"
|
|
"return &log($oException->level(), $oException->message(), $oException->code(), undef, undef, undef, $oException->extra());\n"
|
|
"}\n"
|
|
"\n"
|
|
"push @EXPORT, qw(logException);\n"
|
|
"\n\n\n\n"
|
|
"sub logErrorResult\n"
|
|
"{\n"
|
|
"my $iCode = shift;\n"
|
|
"my $strMessage = shift;\n"
|
|
"my $strResult = shift;\n"
|
|
"\n"
|
|
"confess &log(ERROR, $strMessage . (defined($strResult) ? ': ' . trim($strResult) : ''), $iCode);\n"
|
|
"}\n"
|
|
"\n"
|
|
"push @EXPORT, qw(logErrorResult);\n"
|
|
"\n\n\n\n"
|
|
"sub log\n"
|
|
"{\n"
|
|
"my $strLevel = shift;\n"
|
|
"my $strMessage = shift;\n"
|
|
"my $iCode = shift;\n"
|
|
"my $bSuppressLog = shift;\n"
|
|
"my $iIndent = shift;\n"
|
|
"my $iProcessId = shift;\n"
|
|
"my $rExtra = shift;\n"
|
|
"\n\n"
|
|
"$bSuppressLog = defined($bSuppressLog) ? $bSuppressLog : false;\n"
|
|
"\n\n"
|
|
"if (!defined($rExtra))\n"
|
|
"{\n"
|
|
"$rExtra =\n"
|
|
"{\n"
|
|
"bLogFile => false,\n"
|
|
"bLogConsole => false,\n"
|
|
"};\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"my $strMessageFormat = $strMessage;\n"
|
|
"my $iLogLevelRank = $oLogLevelRank{$strLevel}{rank};\n"
|
|
"\n\n"
|
|
"if ($strLevel eq TEST)\n"
|
|
"{\n"
|
|
"if (!defined($oTestPoint) || !defined($$oTestPoint{lc($strMessage)}))\n"
|
|
"{\n"
|
|
"return;\n"
|
|
"}\n"
|
|
"\n"
|
|
"$iLogLevelRank = $oLogLevelRank{TRACE}{rank} + 1;\n"
|
|
"$strMessageFormat = TEST_ENCLOSE . '-' . $strMessageFormat . '-' . TEST_ENCLOSE;\n"
|
|
"}\n"
|
|
"\n"
|
|
"elsif (!defined($iLogLevelRank))\n"
|
|
"{\n"
|
|
"confess &log(ASSERT, \"log level ${strLevel} does not exist\");\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if (!defined($strMessageFormat))\n"
|
|
"{\n"
|
|
"$strMessageFormat = '(undefined)';\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if ($strLevel eq ASSERT)\n"
|
|
"{\n"
|
|
"$iCode = ERROR_ASSERT;\n"
|
|
"}\n"
|
|
"elsif ($strLevel eq ERROR && !defined($iCode))\n"
|
|
"{\n"
|
|
"$iCode = ERROR_UNKNOWN;\n"
|
|
"}\n"
|
|
"\n"
|
|
"$strMessageFormat = (defined($iCode) ? sprintf('[%03d]: ', $iCode) : '') . $strMessageFormat;\n"
|
|
"\n\n"
|
|
"if (defined($iIndent))\n"
|
|
"{\n"
|
|
"my $strIndent = ' ' x $iIndent;\n"
|
|
"$strMessageFormat =~ s/\\n/\\n${strIndent}/g;\n"
|
|
"}\n"
|
|
"else\n"
|
|
"{\n"
|
|
"\n"
|
|
"$bLogTimestamp ?\n"
|
|
"$strMessageFormat =~ s/\\n/\\n /g :\n"
|
|
"$strMessageFormat =~ s/\\n/\\n /g\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if ($strLevel eq TRACE || $strLevel eq TEST)\n"
|
|
"{\n"
|
|
"$strMessageFormat =~ s/\\n/\\n /g;\n"
|
|
"$strMessageFormat = ' ' . $strMessageFormat;\n"
|
|
"}\n"
|
|
"elsif ($strLevel eq DEBUG)\n"
|
|
"{\n"
|
|
"$strMessageFormat =~ s/\\n/\\n /g;\n"
|
|
"$strMessageFormat = ' ' . $strMessageFormat;\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime(time);\n"
|
|
"\n\n\n"
|
|
"my $strDisplayLevel = ($bLogWarnOnError && $strLevel eq ERROR ? WARN : $strLevel);\n"
|
|
"my $iLogDisplayLevelRank = ($bLogWarnOnError && $strLevel eq ERROR ? $oLogLevelRank{$strDisplayLevel}{rank} : $iLogLevelRank);\n"
|
|
"\n"
|
|
"$strMessageFormat =\n"
|
|
"($bLogTimestamp ? timestampFormat() . sprintf('.%03d ', (gettimeofday() - int(gettimeofday())) * 1000) : '') .\n"
|
|
"sprintf('P%0*d', $iLogProcessSize, defined($iProcessId) ? $iProcessId : 0) .\n"
|
|
"(' ' x (7 - length($strDisplayLevel))) . \"${strDisplayLevel}: ${strMessageFormat}\\n\";\n"
|
|
"\n\n"
|
|
"if (!$bLogDisable)\n"
|
|
"{\n"
|
|
"\n"
|
|
"if (!$rExtra->{bLogConsole} && $iLogDisplayLevelRank <= $oLogLevelRank{$strLogLevelStdErr}{rank})\n"
|
|
"{\n"
|
|
"if ($strLogLevelStdErr ne PROTOCOL)\n"
|
|
"{\n"
|
|
"syswrite(*STDERR, $strDisplayLevel . (defined($iCode) ? sprintf(' [%03d]: ', $iCode) : '') . ': ');\n"
|
|
"}\n"
|
|
"\n"
|
|
"syswrite(*STDERR, \"${strMessage}\\n\");\n"
|
|
"$rExtra->{bLogConsole} = true;\n"
|
|
"}\n"
|
|
"\n"
|
|
"elsif (!$rExtra->{bLogConsole} && $iLogDisplayLevelRank <= $oLogLevelRank{$strLogLevelConsole}{rank} ||\n"
|
|
"$bTest && $strLevel eq TEST)\n"
|
|
"{\n"
|
|
"if (!$bSuppressLog)\n"
|
|
"{\n"
|
|
"syswrite(*STDOUT, $strMessageFormat);\n"
|
|
"\n\n\n\n\n\n\n\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if ($bTest && $strLevel eq TEST && $fTestDelay > 0)\n"
|
|
"{\n"
|
|
"usleep($fTestDelay * 1000000);\n"
|
|
"}\n"
|
|
"\n"
|
|
"$rExtra->{bLogConsole} = true;\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if (!$rExtra->{bLogLogFile} && $iLogDisplayLevelRank <= $oLogLevelRank{$strLogLevelFile}{rank})\n"
|
|
"{\n"
|
|
"if (defined($hLogFile) || (defined($strLogLevelFile) && $strLogLevelFile ne OFF))\n"
|
|
"{\n"
|
|
"if (!$bSuppressLog)\n"
|
|
"{\n"
|
|
"if (defined($hLogFile))\n"
|
|
"{\n"
|
|
"logBanner();\n"
|
|
"syswrite($hLogFile, $strMessageFormat);\n"
|
|
"}\n"
|
|
"else\n"
|
|
"{\n"
|
|
"$strLogFileCache .= $strMessageFormat;\n"
|
|
"}\n"
|
|
"\n"
|
|
"if ($strDisplayLevel eq ASSERT ||\n"
|
|
"($strDisplayLevel eq ERROR && ($strLogLevelFile eq DEBUG || $strLogLevelFile eq TRACE)))\n"
|
|
"{\n"
|
|
"my $strStackTrace = longmess() . \"\\n\";\n"
|
|
"$strStackTrace =~ s/\\n/\\n /g;\n"
|
|
"\n"
|
|
"if (defined($hLogFile))\n"
|
|
"{\n"
|
|
"syswrite($hLogFile, $strStackTrace);\n"
|
|
"}\n"
|
|
"else\n"
|
|
"{\n"
|
|
"$strLogFileCache .= $strStackTrace;\n"
|
|
"}\n"
|
|
"}\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n"
|
|
"$rExtra->{bLogFile} = true;\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if (defined($iCode))\n"
|
|
"{\n"
|
|
"$oErrorLast = new pgBackRest::Common::Exception($strLevel, $iCode, $strMessage, longmess(), $rExtra);\n"
|
|
"return $oErrorLast;\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"return $strMessage;\n"
|
|
"}\n"
|
|
"\n"
|
|
"push @EXPORT, qw(log);\n"
|
|
"\n\n\n\n"
|
|
"sub logErrorLast\n"
|
|
"{\n"
|
|
"return $oErrorLast;\n"
|
|
"}\n"
|
|
"\n"
|
|
"push @EXPORT, qw(logErrorLast);\n"
|
|
"\n\n\n\n\n\n"
|
|
"sub testSet\n"
|
|
"{\n"
|
|
"my $bTestParam = shift;\n"
|
|
"my $fTestDelayParam = shift;\n"
|
|
"my $oTestPointParam = shift;\n"
|
|
"\n\n"
|
|
"$bTest = defined($bTestParam) ? $bTestParam : false;\n"
|
|
"$fTestDelay = defined($bTestParam) ? $fTestDelayParam : $fTestDelay;\n"
|
|
"$oTestPoint = $oTestPointParam;\n"
|
|
"\n\n"
|
|
"if ($bTest && !defined($fTestDelay))\n"
|
|
"{\n"
|
|
"confess &log(ASSERT, 'iTestDelay must be provided when bTest is true');\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if (!($fTestDelay >= 0 && $fTestDelay <= 600))\n"
|
|
"{\n"
|
|
"confess &log(ERROR, 'test-delay must be between 1 and 600 seconds');\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n"
|
|
"push @EXPORT, qw(testSet);\n"
|
|
"\n\n\n\n"
|
|
"sub testCheck\n"
|
|
"{\n"
|
|
"my $strLog = shift;\n"
|
|
"my $strTest = shift;\n"
|
|
"\n"
|
|
"return index($strLog, TEST_ENCLOSE . '-' . $strTest . '-' . TEST_ENCLOSE) != -1;\n"
|
|
"}\n"
|
|
"\n"
|
|
"push @EXPORT, qw(testCheck);\n"
|
|
"\n\n\n\n"
|
|
"sub logLevel\n"
|
|
"{\n"
|
|
"return ($strLogLevelFile, $strLogLevelConsole, $strLogLevelStdErr, $bLogTimestamp);\n"
|
|
"}\n"
|
|
"\n"
|
|
"push @EXPORT, qw(logLevel);\n"
|
|
"\n\n\n\n"
|
|
"sub logFileCacheClear\n"
|
|
"{\n"
|
|
"undef($strLogFileCache);\n"
|
|
"}\n"
|
|
"\n"
|
|
"push @EXPORT, qw(logFileCacheClear);\n"
|
|
"\n\n\n\n"
|
|
"sub logFileCache\n"
|
|
"{\n"
|
|
"return $strLogFileCache;\n"
|
|
"}\n"
|
|
"\n"
|
|
"push @EXPORT, qw(logFileCache);\n"
|
|
"\n"
|
|
"1;\n"
|
|
},
|
|
{
|
|
.name = "pgBackRest/Common/String.pm",
|
|
.data =
|
|
"\n\n\n"
|
|
"package pgBackRest::Common::String;\n"
|
|
"\n"
|
|
"use strict;\n"
|
|
"use warnings FATAL => qw(all);\n"
|
|
"use Carp qw(confess longmess);\n"
|
|
"\n"
|
|
"use Exporter qw(import);\n"
|
|
"our @EXPORT = qw();\n"
|
|
"use File::Basename qw(dirname);\n"
|
|
"\n\n\n\n\n\n"
|
|
"sub trim\n"
|
|
"{\n"
|
|
"my $strBuffer = shift;\n"
|
|
"\n"
|
|
"if (!defined($strBuffer))\n"
|
|
"{\n"
|
|
"return;\n"
|
|
"}\n"
|
|
"\n"
|
|
"$strBuffer =~ s/^\\s+|\\s+$//g;\n"
|
|
"\n"
|
|
"return $strBuffer;\n"
|
|
"}\n"
|
|
"\n"
|
|
"push @EXPORT, qw(trim);\n"
|
|
"\n\n\n\n"
|
|
"sub coalesce\n"
|
|
"{\n"
|
|
"foreach my $strParam (@_)\n"
|
|
"{\n"
|
|
"if (defined($strParam))\n"
|
|
"{\n"
|
|
"return $strParam;\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n"
|
|
"return;\n"
|
|
"}\n"
|
|
"\n"
|
|
"push @EXPORT, qw(coalesce);\n"
|
|
"\n\n\n\n\n\n"
|
|
"sub commonPrefix\n"
|
|
"{\n"
|
|
"my $strString1 = shift;\n"
|
|
"my $strString2 = shift;\n"
|
|
"\n"
|
|
"my $iCommonLen = 0;\n"
|
|
"my $iCompareLen = length($strString1) < length($strString2) ? length($strString1) : length($strString2);\n"
|
|
"\n"
|
|
"for (my $iIndex = 0; $iIndex < $iCompareLen; $iIndex++)\n"
|
|
"{\n"
|
|
"if (substr($strString1, $iIndex, 1) ne substr($strString2, $iIndex, 1))\n"
|
|
"{\n"
|
|
"last;\n"
|
|
"}\n"
|
|
"\n"
|
|
"$iCommonLen++;\n"
|
|
"}\n"
|
|
"\n"
|
|
"return $iCommonLen;\n"
|
|
"}\n"
|
|
"\n"
|
|
"push @EXPORT, qw(commonPrefix);\n"
|
|
"\n\n\n\n\n\n"
|
|
"sub boolFormat\n"
|
|
"{\n"
|
|
"return shift() ? 'true' : 'false';\n"
|
|
"}\n"
|
|
"\n"
|
|
"push @EXPORT, qw(boolFormat);\n"
|
|
"\n\n\n\n\n\n"
|
|
"sub fileSizeFormat\n"
|
|
"{\n"
|
|
"my $lFileSize = shift;\n"
|
|
"\n"
|
|
"if ($lFileSize < 1024)\n"
|
|
"{\n"
|
|
"return $lFileSize . 'B';\n"
|
|
"}\n"
|
|
"\n"
|
|
"if ($lFileSize < (1024 * 1024))\n"
|
|
"{\n"
|
|
"return (int($lFileSize / 102.4) / 10) . 'KB';\n"
|
|
"}\n"
|
|
"\n"
|
|
"if ($lFileSize < (1024 * 1024 * 1024))\n"
|
|
"{\n"
|
|
"return (int($lFileSize / 1024 / 102.4) / 10) . 'MB';\n"
|
|
"}\n"
|
|
"\n"
|
|
"return (int($lFileSize / 1024 / 1024 / 102.4) / 10) . 'GB';\n"
|
|
"}\n"
|
|
"\n"
|
|
"push @EXPORT, qw(fileSizeFormat);\n"
|
|
"\n\n\n\n\n\n"
|
|
"sub timestampFormat\n"
|
|
"{\n"
|
|
"my $strFormat = shift;\n"
|
|
"my $lTime = shift;\n"
|
|
"\n"
|
|
"if (!defined($strFormat))\n"
|
|
"{\n"
|
|
"$strFormat = '%4d-%02d-%02d %02d:%02d:%02d';\n"
|
|
"}\n"
|
|
"\n"
|
|
"if (!defined($lTime))\n"
|
|
"{\n"
|
|
"$lTime = time();\n"
|
|
"}\n"
|
|
"\n"
|
|
"my ($iSecond, $iMinute, $iHour, $iMonthDay, $iMonth, $iYear, $iWeekDay, $iYearDay, $bIsDst) = localtime($lTime);\n"
|
|
"\n"
|
|
"if ($strFormat eq \"%4d\")\n"
|
|
"{\n"
|
|
"return sprintf($strFormat, $iYear + 1900)\n"
|
|
"}\n"
|
|
"else\n"
|
|
"{\n"
|
|
"return sprintf($strFormat, $iYear + 1900, $iMonth + 1, $iMonthDay, $iHour, $iMinute, $iSecond);\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n"
|
|
"push @EXPORT, qw(timestampFormat);\n"
|
|
"\n\n\n\n"
|
|
"sub timestampFileFormat\n"
|
|
"{\n"
|
|
"my $strFormat = shift;\n"
|
|
"my $lTime = shift;\n"
|
|
"\n"
|
|
"return timestampFormat(defined($strFormat) ? $strFormat : '%4d%02d%02d-%02d%02d%02d', $lTime);\n"
|
|
"}\n"
|
|
"\n"
|
|
"push @EXPORT, qw(timestampFileFormat);\n"
|
|
"\n\n\n\n"
|
|
"sub stringSplit\n"
|
|
"{\n"
|
|
"my $strString = shift;\n"
|
|
"my $strChar = shift;\n"
|
|
"my $iLength = shift;\n"
|
|
"\n"
|
|
"if (length($strString) <= $iLength)\n"
|
|
"{\n"
|
|
"return $strString, undef;\n"
|
|
"}\n"
|
|
"\n"
|
|
"my $iPos = index($strString, $strChar);\n"
|
|
"\n"
|
|
"if ($iPos == -1)\n"
|
|
"{\n"
|
|
"return $strString, undef;\n"
|
|
"}\n"
|
|
"\n"
|
|
"my $iNewPos = $iPos;\n"
|
|
"\n"
|
|
"while ($iNewPos != -1 && $iNewPos + 1 < $iLength)\n"
|
|
"{\n"
|
|
"$iPos = $iNewPos;\n"
|
|
"$iNewPos = index($strString, $strChar, $iPos + 1);\n"
|
|
"}\n"
|
|
"\n"
|
|
"return substr($strString, 0, $iPos + 1), substr($strString, $iPos + 1);\n"
|
|
"}\n"
|
|
"\n"
|
|
"push @EXPORT, qw(stringSplit);\n"
|
|
"\n\n\n\n\n\n"
|
|
"sub regexPrefix\n"
|
|
"{\n"
|
|
"my $strExpression = shift;\n"
|
|
"my $strPrefix;\n"
|
|
"\n\n"
|
|
"if (defined($strExpression) && $strExpression =~ /^\\^/)\n"
|
|
"{\n"
|
|
"($strPrefix) = substr($strExpression, 1) =~ /^[^\\.\\^\\$\\*\\+\\-\\?\\(\\)\\[\\]\\{\\}\\\\\\|\\ ]+/g;\n"
|
|
"}\n"
|
|
"\n"
|
|
"return $strPrefix;\n"
|
|
"}\n"
|
|
"\n"
|
|
"push @EXPORT, qw(regexPrefix);\n"
|
|
"\n"
|
|
"1;\n"
|
|
},
|
|
{
|
|
.name = "pgBackRest/Common/Wait.pm",
|
|
.data =
|
|
"\n\n\n"
|
|
"package pgBackRest::Common::Wait;\n"
|
|
"\n"
|
|
"use strict;\n"
|
|
"use warnings FATAL => qw(all);\n"
|
|
"use Carp qw(confess);\n"
|
|
"\n"
|
|
"use Exporter qw(import);\n"
|
|
"our @EXPORT = qw();\n"
|
|
"use File::Basename qw(dirname);\n"
|
|
"use POSIX qw(ceil);\n"
|
|
"use Time::HiRes qw(gettimeofday usleep);\n"
|
|
"\n"
|
|
"use pgBackRest::Common::Log;\n"
|
|
"\n\n\n\n"
|
|
"use constant WAIT_TIME_MINIMUM => .1;\n"
|
|
"push @EXPORT, qw(WAIT_TIME_MINIMUM);\n"
|
|
"\n\n\n\n"
|
|
"sub waitRemainder\n"
|
|
"{\n"
|
|
"my $bWait = shift;\n"
|
|
"\n"
|
|
"my $lTimeBegin = gettimeofday();\n"
|
|
"\n"
|
|
"if (!defined($bWait) || $bWait)\n"
|
|
"{\n"
|
|
"my $lSleepMs = ceil(((int($lTimeBegin) + 1.05) - $lTimeBegin) * 1000);\n"
|
|
"\n"
|
|
"usleep($lSleepMs * 1000);\n"
|
|
"\n"
|
|
"&log(TRACE, \"WAIT_REMAINDER: slept ${lSleepMs}ms: begin ${lTimeBegin}, end \" . gettimeofday());\n"
|
|
"}\n"
|
|
"\n"
|
|
"return int($lTimeBegin);\n"
|
|
"}\n"
|
|
"\n"
|
|
"push @EXPORT, qw(waitRemainder);\n"
|
|
"\n\n\n\n"
|
|
"sub waitHiRes\n"
|
|
"{\n"
|
|
"my $fSecond = shift;\n"
|
|
"\n"
|
|
"return usleep($fSecond * 1000000);\n"
|
|
"}\n"
|
|
"\n"
|
|
"push @EXPORT, qw(waitHiRes);\n"
|
|
"\n\n\n\n"
|
|
"sub waitInit\n"
|
|
"{\n"
|
|
"my $fWaitTime = shift;\n"
|
|
"my $fSleep = shift;\n"
|
|
"\n\n"
|
|
"my $oWait = {};\n"
|
|
"\n\n"
|
|
"if (!defined($fWaitTime) || $fWaitTime == 0)\n"
|
|
"{\n"
|
|
"return;\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if ($fWaitTime < .1)\n"
|
|
"{\n"
|
|
"confess &log(ASSERT, 'fWaitTime cannot be < .1');\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if (!defined($fSleep))\n"
|
|
"{\n"
|
|
"if ($fWaitTime >= 1)\n"
|
|
"{\n"
|
|
"$$oWait{sleep} = .1;\n"
|
|
"}\n"
|
|
"else\n"
|
|
"{\n"
|
|
"$$oWait{sleep} = $fWaitTime / 10;\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n"
|
|
"else\n"
|
|
"{\n"
|
|
"\n"
|
|
"if ($fSleep >= $fWaitTime)\n"
|
|
"{\n"
|
|
"confess &log(ASSERT, 'fSleep > fWaitTime - this is useless');\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"$$oWait{wait_time} = $fWaitTime;\n"
|
|
"$$oWait{time_begin} = gettimeofday();\n"
|
|
"$$oWait{time_end} = $$oWait{time_begin};\n"
|
|
"\n"
|
|
"return $oWait;\n"
|
|
"}\n"
|
|
"\n"
|
|
"push @EXPORT, qw(waitInit);\n"
|
|
"\n\n\n\n"
|
|
"sub waitMore\n"
|
|
"{\n"
|
|
"my $oWait = shift;\n"
|
|
"\n\n"
|
|
"if (!defined($oWait))\n"
|
|
"{\n"
|
|
"return false;\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"waitHiRes($$oWait{sleep});\n"
|
|
"\n\n"
|
|
"$$oWait{time_end} = gettimeofday();\n"
|
|
"\n\n"
|
|
"if ((gettimeofday() - $$oWait{time_begin}) < $$oWait{wait_time})\n"
|
|
"{\n"
|
|
"return true;\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"my $fSleepNext = $$oWait{sleep} + (defined($$oWait{sleep_prev}) ? $$oWait{sleep_prev} : 0);\n"
|
|
"\n"
|
|
"if ($fSleepNext > $$oWait{wait_time} - ($$oWait{time_end} - $$oWait{time_begin}))\n"
|
|
"{\n"
|
|
"$fSleepNext = ($$oWait{wait_time} - ($$oWait{time_end} - $$oWait{time_begin})) + .001\n"
|
|
"}\n"
|
|
"\n"
|
|
"$$oWait{sleep_prev} = $$oWait{sleep};\n"
|
|
"$$oWait{sleep} = $fSleepNext;\n"
|
|
"\n"
|
|
"return false;\n"
|
|
"}\n"
|
|
"\n"
|
|
"push @EXPORT, qw(waitMore);\n"
|
|
"\n\n\n\n"
|
|
"sub waitInterval\n"
|
|
"{\n"
|
|
"my $oWait = shift;\n"
|
|
"\n\n"
|
|
"if (!defined($oWait))\n"
|
|
"{\n"
|
|
"confess &log(ERROR, \"oWait is not defined\");\n"
|
|
"}\n"
|
|
"\n"
|
|
"return int(($$oWait{time_end} - $$oWait{time_begin}) * 1000) / 1000;\n"
|
|
"}\n"
|
|
"\n"
|
|
"push @EXPORT, qw(waitInterval);\n"
|
|
"\n"
|
|
"1;\n"
|
|
},
|
|
{
|
|
.name = "pgBackRest/Common/Xml.pm",
|
|
.data =
|
|
"\n\n\n"
|
|
"package pgBackRest::Common::Xml;\n"
|
|
"\n"
|
|
"use strict;\n"
|
|
"use warnings FATAL => qw(all);\n"
|
|
"use Carp qw(confess);\n"
|
|
"use English '-no_match_vars';\n"
|
|
"\n"
|
|
"use Exporter qw(import);\n"
|
|
"our @EXPORT = qw();\n"
|
|
"use XML::LibXML;\n"
|
|
"\n"
|
|
"use pgBackRest::Common::Exception;\n"
|
|
"use pgBackRest::Common::Log;\n"
|
|
"\n\n\n\n"
|
|
"use constant XML_HEADER => '<?xml version=\"1.0\" encoding=\"UTF-8\"?>';\n"
|
|
"push @EXPORT, qw(XML_HEADER);\n"
|
|
"\n\n\n\n"
|
|
"sub xmlFromText\n"
|
|
"{\n"
|
|
"my $strText = shift;\n"
|
|
"\n"
|
|
"return XML::LibXML::Text->new($strText)->toString();\n"
|
|
"}\n"
|
|
"\n"
|
|
"push @EXPORT, qw(xmlFromText);\n"
|
|
"\n\n\n\n"
|
|
"sub xmlParse\n"
|
|
"{\n"
|
|
"my $rstrXml = shift;\n"
|
|
"\n"
|
|
"my $oXml = XML::LibXML->load_xml(string => $rstrXml)->documentElement();\n"
|
|
"\n"
|
|
"return $oXml;\n"
|
|
"}\n"
|
|
"\n"
|
|
"push @EXPORT, qw(xmlParse);\n"
|
|
"\n\n\n\n"
|
|
"sub xmlTagChildren\n"
|
|
"{\n"
|
|
"my $oXml = shift;\n"
|
|
"my $strTag = shift;\n"
|
|
"\n"
|
|
"return $oXml->getChildrenByTagName($strTag);\n"
|
|
"}\n"
|
|
"\n"
|
|
"push @EXPORT, qw(xmlTagChildren);\n"
|
|
"\n\n\n\n"
|
|
"sub xmlTagText\n"
|
|
"{\n"
|
|
"my $oXml = shift;\n"
|
|
"my $strTag = shift;\n"
|
|
"my $bRequired = shift;\n"
|
|
"\n\n\n"
|
|
"my @oyTag = $oXml->getElementsByTagName($strTag);\n"
|
|
"\n\n"
|
|
"if (@oyTag > 1)\n"
|
|
"{\n"
|
|
"confess &log(ERROR, @oyTag . \" '${strTag}' tag(s) exist, but only one was expected\", ERROR_FORMAT);\n"
|
|
"}\n"
|
|
"elsif (@oyTag == 0)\n"
|
|
"{\n"
|
|
"if (!defined($bRequired) || $bRequired)\n"
|
|
"{\n"
|
|
"confess &log(ERROR, \"tag '${strTag}' does not exist\", ERROR_FORMAT);\n"
|
|
"}\n"
|
|
"}\n"
|
|
"else\n"
|
|
"{\n"
|
|
"return $oyTag[0]->textContent();\n"
|
|
"}\n"
|
|
"\n"
|
|
"return;\n"
|
|
"}\n"
|
|
"\n"
|
|
"push @EXPORT, qw(xmlTagText);\n"
|
|
"\n\n\n\n"
|
|
"sub xmlTagBool\n"
|
|
"{\n"
|
|
"my $oXml = shift;\n"
|
|
"my $strTag = shift;\n"
|
|
"my $bRequired = shift;\n"
|
|
"\n\n\n"
|
|
"my $strContent = xmlTagText($oXml, $strTag, $bRequired);\n"
|
|
"\n"
|
|
"if (defined($strContent))\n"
|
|
"{\n"
|
|
"if ($strContent eq 'true')\n"
|
|
"{\n"
|
|
"return true;\n"
|
|
"}\n"
|
|
"elsif ($strContent eq 'false')\n"
|
|
"{\n"
|
|
"return false;\n"
|
|
"}\n"
|
|
"else\n"
|
|
"{\n"
|
|
"confess &log(ERROR, \"invalid boolean value '${strContent}' for tag '${strTag}'\", ERROR_FORMAT);\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n"
|
|
"return;\n"
|
|
"}\n"
|
|
"\n"
|
|
"push @EXPORT, qw(xmlTagBool);\n"
|
|
"\n\n\n\n"
|
|
"sub xmlTagInt\n"
|
|
"{\n"
|
|
"my $oXml = shift;\n"
|
|
"my $strTag = shift;\n"
|
|
"my $bRequired = shift;\n"
|
|
"\n\n\n"
|
|
"my $iContent = xmlTagText($oXml, $strTag, $bRequired);\n"
|
|
"\n"
|
|
"if (defined($iContent))\n"
|
|
"{\n"
|
|
"eval\n"
|
|
"{\n"
|
|
"$iContent = $iContent + 0;\n"
|
|
"return 1;\n"
|
|
"}\n"
|
|
"or do\n"
|
|
"{\n"
|
|
"confess &log(ERROR, \"invalid integer value '${iContent}' for tag '${strTag}'\", ERROR_FORMAT);\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n"
|
|
"return $iContent;\n"
|
|
"}\n"
|
|
"\n"
|
|
"push @EXPORT, qw(xmlTagInt);\n"
|
|
"\n"
|
|
"1;\n"
|
|
},
|
|
{
|
|
.name = "pgBackRest/Config/Config.pm",
|
|
.data =
|
|
"\n\n\n"
|
|
"package pgBackRest::Config::Config;\n"
|
|
"\n"
|
|
"use strict;\n"
|
|
"use warnings FATAL => qw(all);\n"
|
|
"use Carp qw(confess);\n"
|
|
"use English '-no_match_vars';\n"
|
|
"\n"
|
|
"use Exporter qw(import);\n"
|
|
"our @EXPORT = qw();\n"
|
|
"use JSON::PP;\n"
|
|
"\n"
|
|
"use pgBackRest::Common::Exception;\n"
|
|
"use pgBackRest::Common::Ini;\n"
|
|
"use pgBackRest::Common::Io::Base;\n"
|
|
"use pgBackRest::Common::Log;\n"
|
|
"use pgBackRest::Common::String;\n"
|
|
"use pgBackRest::Common::Wait;\n"
|
|
"use pgBackRest::LibC qw(:config :configDefine);\n"
|
|
"use pgBackRest::Version;\n"
|
|
"\n\n\n\n"
|
|
"push(@EXPORT, @{$pgBackRest::LibC::EXPORT_TAGS{config}});\n"
|
|
"push(@EXPORT, @{$pgBackRest::LibC::EXPORT_TAGS{configDefine}});\n"
|
|
"\n\n\n\n"
|
|
"use constant CFGDEF_SOURCE_CONFIG => 'config';\n"
|
|
"push @EXPORT, qw(CFGDEF_SOURCE_CONFIG);\n"
|
|
"use constant CFGDEF_SOURCE_PARAM => 'param';\n"
|
|
"push @EXPORT, qw(CFGDEF_SOURCE_PARAM);\n"
|
|
"use constant CFGDEF_SOURCE_DEFAULT => 'default';\n"
|
|
"push @EXPORT, qw(CFGDEF_SOURCE_DEFAULT);\n"
|
|
"\n\n\n\n"
|
|
"use constant CFGDEF_SECTION_GLOBAL => 'global';\n"
|
|
"push @EXPORT, qw(CFGDEF_SECTION_GLOBAL);\n"
|
|
"use constant CFGDEF_SECTION_STANZA => 'stanza';\n"
|
|
"push @EXPORT, qw(CFGDEF_SECTION_STANZA);\n"
|
|
"\n\n\n\n"
|
|
"my %oOption;\n"
|
|
"my $strCommand;\n"
|
|
"my $bInitLog = false;\n"
|
|
"\n\n\n\n"
|
|
"sub configLogging\n"
|
|
"{\n"
|
|
"my $bLogInitForce = shift;\n"
|
|
"\n"
|
|
"if ($bInitLog || (defined($bLogInitForce) && $bLogInitForce))\n"
|
|
"{\n"
|
|
"logLevelSet(\n"
|
|
"cfgOptionValid(CFGOPT_LOG_LEVEL_FILE) ? cfgOption(CFGOPT_LOG_LEVEL_FILE) : OFF,\n"
|
|
"cfgOptionValid(CFGOPT_LOG_LEVEL_CONSOLE) ? cfgOption(CFGOPT_LOG_LEVEL_CONSOLE) : OFF,\n"
|
|
"cfgOptionValid(CFGOPT_LOG_LEVEL_STDERR) ? cfgOption(CFGOPT_LOG_LEVEL_STDERR) : OFF,\n"
|
|
"cfgOptionValid(CFGOPT_LOG_TIMESTAMP) ? cfgOption(CFGOPT_LOG_TIMESTAMP) : undef,\n"
|
|
"cfgOptionValid(CFGOPT_PROCESS_MAX) ? cfgOption(CFGOPT_PROCESS_MAX) : undef);\n"
|
|
"\n"
|
|
"$bInitLog = true;\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n"
|
|
"push @EXPORT, qw(configLogging);\n"
|
|
"\n\n\n\n\n\n"
|
|
"sub configLoad\n"
|
|
"{\n"
|
|
"my $bInitLogging = shift;\n"
|
|
"my $strBackRestBin = shift;\n"
|
|
"my $strCommandName = shift;\n"
|
|
"my $rstrConfigJson = shift;\n"
|
|
"\n\n"
|
|
"projectBinSet($strBackRestBin);\n"
|
|
"\n\n"
|
|
"$strCommand = $strCommandName;\n"
|
|
"\n"
|
|
"eval\n"
|
|
"{\n"
|
|
"%oOption = %{(JSON::PP->new()->allow_nonref())->decode($$rstrConfigJson)};\n"
|
|
"return true;\n"
|
|
"}\n"
|
|
"or do\n"
|
|
"{\n"
|
|
"confess &log(ASSERT, \"unable to parse config JSON\");\n"
|
|
"};\n"
|
|
"\n\n"
|
|
"for (my $iOptionId = 0; $iOptionId < cfgOptionTotal(); $iOptionId++)\n"
|
|
"{\n"
|
|
"my $strOptionName = cfgOptionName($iOptionId);\n"
|
|
"\n\n"
|
|
"if (defined($oOption{$strOptionName}))\n"
|
|
"{\n"
|
|
"\n"
|
|
"if (cfgDefOptionType($iOptionId) eq CFGDEF_TYPE_BOOLEAN && defined($oOption{$strOptionName}{value}))\n"
|
|
"{\n"
|
|
"$oOption{$strOptionName}{value} = $oOption{$strOptionName}{value} eq INI_TRUE ? true : false;\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n"
|
|
"else\n"
|
|
"{\n"
|
|
"$oOption{$strOptionName}{valid} = false;\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"if (!defined($bInitLogging) || $bInitLogging)\n"
|
|
"{\n"
|
|
"configLogging(true);\n"
|
|
"}\n"
|
|
"\n"
|
|
"return true;\n"
|
|
"}\n"
|
|
"\n"
|
|
"push @EXPORT, qw(configLoad);\n"
|
|
"\n\n\n\n"
|
|
"sub cfgOptionIdFromIndex\n"
|
|
"{\n"
|
|
"my $iOptionId = shift;\n"
|
|
"my $iIndex = shift;\n"
|
|
"\n\n"
|
|
"$iIndex = defined($iIndex) ? $iIndex : 1;\n"
|
|
"my $strPrefix = cfgDefOptionPrefix($iOptionId);\n"
|
|
"\n"
|
|
"if (!defined($strPrefix))\n"
|
|
"{\n"
|
|
"if ($iIndex > 1)\n"
|
|
"{\n"
|
|
"confess &log(ASSERT, \"'\" . cfgOptionName($iOptionId) . \"' option does not allow indexing\");\n"
|
|
"}\n"
|
|
"\n"
|
|
"return $iOptionId;\n"
|
|
"}\n"
|
|
"\n"
|
|
"return cfgOptionId(\"${strPrefix}${iIndex}\" . substr(cfgOptionName($iOptionId), index(cfgOptionName($iOptionId), '-')));\n"
|
|
"}\n"
|
|
"\n"
|
|
"push @EXPORT, qw(cfgOptionIdFromIndex);\n"
|
|
"\n\n\n\n"
|
|
"sub cfgOptionSource\n"
|
|
"{\n"
|
|
"my $iOptionId = shift;\n"
|
|
"\n"
|
|
"cfgOptionValid($iOptionId, true);\n"
|
|
"\n"
|
|
"return $oOption{cfgOptionName($iOptionId)}{source};\n"
|
|
"}\n"
|
|
"\n"
|
|
"push @EXPORT, qw(cfgOptionSource);\n"
|
|
"\n\n\n\n"
|
|
"sub cfgOptionValid\n"
|
|
"{\n"
|
|
"my $iOptionId = shift;\n"
|
|
"my $bError = shift;\n"
|
|
"\n\n"
|
|
"my $iCommandId;\n"
|
|
"\n"
|
|
"if (defined($strCommand))\n"
|
|
"{\n"
|
|
"$iCommandId = cfgCommandId($strCommand);\n"
|
|
"}\n"
|
|
"\n"
|
|
"if (defined($iCommandId) && cfgDefOptionValid($iCommandId, $iOptionId))\n"
|
|
"{\n"
|
|
"return true;\n"
|
|
"}\n"
|
|
"\n"
|
|
"if (defined($bError) && $bError)\n"
|
|
"{\n"
|
|
"my $strOption = cfgOptionName($iOptionId);\n"
|
|
"\n"
|
|
"if (!defined($oOption{$strOption}))\n"
|
|
"{\n"
|
|
"confess &log(ASSERT, \"option '${strOption}' does not exist\");\n"
|
|
"}\n"
|
|
"\n"
|
|
"confess &log(ASSERT, \"option '${strOption}' not valid for command '\" . cfgCommandName(cfgCommandGet()) . \"'\");\n"
|
|
"}\n"
|
|
"\n"
|
|
"return false;\n"
|
|
"}\n"
|
|
"\n"
|
|
"push @EXPORT, qw(cfgOptionValid);\n"
|
|
"\n\n\n\n"
|
|
"sub cfgOption\n"
|
|
"{\n"
|
|
"my $iOptionId = shift;\n"
|
|
"my $bRequired = shift;\n"
|
|
"\n"
|
|
"cfgOptionValid($iOptionId, true);\n"
|
|
"\n"
|
|
"my $strOption = cfgOptionName($iOptionId);\n"
|
|
"\n"
|
|
"if (!defined($oOption{$strOption}{value}) && (!defined($bRequired) || $bRequired))\n"
|
|
"{\n"
|
|
"confess &log(ASSERT, \"option ${strOption} is required\");\n"
|
|
"}\n"
|
|
"\n"
|
|
"return $oOption{$strOption}{value};\n"
|
|
"}\n"
|
|
"\n"
|
|
"push @EXPORT, qw(cfgOption);\n"
|
|
"\n\n\n\n"
|
|
"sub cfgOptionDefault\n"
|
|
"{\n"
|
|
"my $iOptionId = shift;\n"
|
|
"\n"
|
|
"cfgOptionValid($iOptionId, true);\n"
|
|
"\n"
|
|
"return cfgDefOptionDefault(cfgCommandId($strCommand), $iOptionId);\n"
|
|
"}\n"
|
|
"\n"
|
|
"push @EXPORT, qw(cfgOptionDefault);\n"
|
|
"\n\n\n\n"
|
|
"sub cfgOptionSet\n"
|
|
"{\n"
|
|
"my $iOptionId = shift;\n"
|
|
"my $oValue = shift;\n"
|
|
"my $bForce = shift;\n"
|
|
"\n"
|
|
"my $strOption = cfgOptionName($iOptionId);\n"
|
|
"\n"
|
|
"if (!cfgOptionValid($iOptionId, !defined($bForce) || !$bForce))\n"
|
|
"{\n"
|
|
"$oOption{$strOption}{valid} = true;\n"
|
|
"}\n"
|
|
"\n"
|
|
"$oOption{$strOption}{source} = CFGDEF_SOURCE_PARAM;\n"
|
|
"$oOption{$strOption}{value} = $oValue;\n"
|
|
"}\n"
|
|
"\n"
|
|
"push @EXPORT, qw(cfgOptionSet);\n"
|
|
"\n\n\n\n"
|
|
"sub cfgOptionTest\n"
|
|
"{\n"
|
|
"my $iOptionId = shift;\n"
|
|
"my $strValue = shift;\n"
|
|
"\n"
|
|
"if (!cfgOptionValid($iOptionId))\n"
|
|
"{\n"
|
|
"return false;\n"
|
|
"}\n"
|
|
"\n"
|
|
"if (defined($strValue))\n"
|
|
"{\n"
|
|
"return cfgOption($iOptionId) eq $strValue ? true : false;\n"
|
|
"}\n"
|
|
"\n"
|
|
"return defined($oOption{cfgOptionName($iOptionId)}{value}) ? true : false;\n"
|
|
"}\n"
|
|
"\n"
|
|
"push @EXPORT, qw(cfgOptionTest);\n"
|
|
"\n\n\n\n"
|
|
"sub cfgCommandGet\n"
|
|
"{\n"
|
|
"return cfgCommandId($strCommand);\n"
|
|
"}\n"
|
|
"\n"
|
|
"push @EXPORT, qw(cfgCommandGet);\n"
|
|
"\n\n\n\n"
|
|
"sub cfgCommandTest\n"
|
|
"{\n"
|
|
"my $iCommandIdTest = shift;\n"
|
|
"\n"
|
|
"return cfgCommandName($iCommandIdTest) eq $strCommand;\n"
|
|
"}\n"
|
|
"\n"
|
|
"push @EXPORT, qw(cfgCommandTest);\n"
|
|
"\n\n\n\n"
|
|
"sub cfgCommandSet\n"
|
|
"{\n"
|
|
"my $iCommandId = shift;\n"
|
|
"\n"
|
|
"$strCommand = cfgCommandName($iCommandId);\n"
|
|
"}\n"
|
|
"\n"
|
|
"push @EXPORT, qw(cfgCommandSet);\n"
|
|
"\n\n\n\n\n\n"
|
|
"sub cfgCommandWrite\n"
|
|
"{\n"
|
|
"my $iNewCommandId = shift;\n"
|
|
"my $bIncludeConfig = shift;\n"
|
|
"my $strExeString = shift;\n"
|
|
"my $bIncludeCommand = shift;\n"
|
|
"my $oOptionOverride = shift;\n"
|
|
"my $bDisplayOnly = shift;\n"
|
|
"\n\n"
|
|
"$strExeString = defined($strExeString) ? $strExeString : projectBin();\n"
|
|
"$bIncludeConfig = defined($bIncludeConfig) ? $bIncludeConfig : false;\n"
|
|
"$bIncludeCommand = defined($bIncludeCommand) ? $bIncludeCommand : true;\n"
|
|
"\n\n"
|
|
"for (my $iOptionId = 0; $iOptionId < cfgOptionTotal(); $iOptionId++)\n"
|
|
"{\n"
|
|
"my $strOption = cfgOptionName($iOptionId);\n"
|
|
"my $bSecure = cfgDefOptionSecure($iOptionId);\n"
|
|
"\n\n"
|
|
"next if ($bSecure && !$bDisplayOnly);\n"
|
|
"\n\n"
|
|
"if (defined($oOptionOverride->{$iOptionId}))\n"
|
|
"{\n"
|
|
"if (defined($oOptionOverride->{$iOptionId}{value}))\n"
|
|
"{\n"
|
|
"$strExeString .= cfgCommandWriteOptionFormat(\n"
|
|
"$strOption, false, $bSecure, {value => $oOptionOverride->{$iOptionId}{value}});\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n"
|
|
"elsif (defined($oOptionOverride->{$strOption}))\n"
|
|
"{\n"
|
|
"if (defined($oOptionOverride->{$strOption}{value}))\n"
|
|
"{\n"
|
|
"$strExeString .= cfgCommandWriteOptionFormat(\n"
|
|
"$strOption, false, $bSecure, {value => $oOptionOverride->{$strOption}{value}});\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n"
|
|
"elsif (cfgDefOptionValid($iNewCommandId, $iOptionId) &&\n"
|
|
"defined($oOption{$strOption}{value}) &&\n"
|
|
"($bIncludeConfig ?\n"
|
|
"$oOption{$strOption}{source} ne CFGDEF_SOURCE_DEFAULT : $oOption{$strOption}{source} eq CFGDEF_SOURCE_PARAM))\n"
|
|
"{\n"
|
|
"my $oValue;\n"
|
|
"my $bMulti = false;\n"
|
|
"\n\n"
|
|
"if (ref($oOption{$strOption}{value}) eq 'HASH')\n"
|
|
"{\n"
|
|
"$oValue = $oOption{$strOption}{value};\n"
|
|
"$bMulti = true;\n"
|
|
"}\n"
|
|
"\n"
|
|
"else\n"
|
|
"{\n"
|
|
"$oValue = {value => $oOption{$strOption}{value}};\n"
|
|
"}\n"
|
|
"\n"
|
|
"$strExeString .= cfgCommandWriteOptionFormat($strOption, $bMulti, $bSecure, $oValue);\n"
|
|
"}\n"
|
|
"\n"
|
|
"elsif (cfgDefOptionValid($iNewCommandId, $iOptionId) && $oOption{$strOption}{reset})\n"
|
|
"{\n"
|
|
"$strExeString .= \" --reset-${strOption}\";\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n"
|
|
"if ($bIncludeCommand)\n"
|
|
"{\n"
|
|
"$strExeString .= ' ' . cfgCommandName($iNewCommandId);\n"
|
|
"}\n"
|
|
"\n"
|
|
"return $strExeString;\n"
|
|
"}\n"
|
|
"\n"
|
|
"push @EXPORT, qw(cfgCommandWrite);\n"
|
|
"\n\n"
|
|
"sub cfgCommandWriteOptionFormat\n"
|
|
"{\n"
|
|
"my $strOption = shift;\n"
|
|
"my $bMulti = shift;\n"
|
|
"my $bSecure = shift;\n"
|
|
"my $oValue = shift;\n"
|
|
"\n\n"
|
|
"my $strOptionFormat = '';\n"
|
|
"my $strParam;\n"
|
|
"\n"
|
|
"foreach my $strKey (sort(keys(%$oValue)))\n"
|
|
"{\n"
|
|
"\n"
|
|
"my $strValue = $bSecure ? '<redacted>' : ($bMulti ? \"${strKey}=\" : '') . $$oValue{$strKey};\n"
|
|
"\n\n"
|
|
"if (cfgDefOptionType(cfgOptionId($strOption)) eq CFGDEF_TYPE_BOOLEAN)\n"
|
|
"{\n"
|
|
"$strParam = '--' . ($strValue ? '' : 'no-') . $strOption;\n"
|
|
"}\n"
|
|
"else\n"
|
|
"{\n"
|
|
"$strParam = \"--${strOption}=${strValue}\";\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"$strOptionFormat .= ' ' . (index($strValue, \" \") != -1 ? \"\\\"${strParam}\\\"\" : $strParam);\n"
|
|
"}\n"
|
|
"\n"
|
|
"return $strOptionFormat;\n"
|
|
"}\n"
|
|
"\n"
|
|
"1;\n"
|
|
},
|
|
{
|
|
.name = "pgBackRest/Db.pm",
|
|
.data =
|
|
"\n\n\n"
|
|
"package pgBackRest::Db;\n"
|
|
"\n"
|
|
"use strict;\n"
|
|
"use warnings FATAL => qw(all);\n"
|
|
"use Carp qw(confess);\n"
|
|
"\n"
|
|
"use DBD::Pg ':async';\n"
|
|
"use DBI;\n"
|
|
"use Exporter qw(import);\n"
|
|
"our @EXPORT = qw();\n"
|
|
"use Fcntl qw(O_RDONLY);\n"
|
|
"use File::Basename qw(dirname);\n"
|
|
"\n"
|
|
"use pgBackRest::DbVersion;\n"
|
|
"use pgBackRest::Common::Exception;\n"
|
|
"use pgBackRest::Common::Log;\n"
|
|
"use pgBackRest::Common::String;\n"
|
|
"use pgBackRest::Common::Wait;\n"
|
|
"use pgBackRest::Config::Config;\n"
|
|
"use pgBackRest::Manifest;\n"
|
|
"use pgBackRest::Protocol::Helper;\n"
|
|
"use pgBackRest::Protocol::Storage::Helper;\n"
|
|
"use pgBackRest::Version;\n"
|
|
"\n\n\n\n\n\n"
|
|
"use constant PG_WAL_SIZE_83 => 16777216;\n"
|
|
"\n\n\n\n"
|
|
"use constant DB_BACKUP_ADVISORY_LOCK => '12340078987004321';\n"
|
|
"push @EXPORT, qw(DB_BACKUP_ADVISORY_LOCK);\n"
|
|
"\n\n\n\n\n\n\n\n"
|
|
"my $oPgControlVersionHash =\n"
|
|
"{\n"
|
|
"\n"
|
|
"833 => {200711281 => PG_VERSION_83},\n"
|
|
"843 => {200904091 => PG_VERSION_84},\n"
|
|
"903 =>\n"
|
|
"{\n"
|
|
"201008051 => PG_VERSION_90,\n"
|
|
"201105231 => PG_VERSION_91,\n"
|
|
"},\n"
|
|
"922 => {201204301 => PG_VERSION_92},\n"
|
|
"937 => {201306121 => PG_VERSION_93},\n"
|
|
"942 =>\n"
|
|
"{\n"
|
|
"201409291 => PG_VERSION_94,\n"
|
|
"201510051 => PG_VERSION_95,\n"
|
|
"},\n"
|
|
"960 =>\n"
|
|
"{\n"
|
|
"201608131 => PG_VERSION_96,\n"
|
|
"},\n"
|
|
"1002 =>\n"
|
|
"{\n"
|
|
"201707211 => PG_VERSION_10,\n"
|
|
"},\n"
|
|
"1100 =>\n"
|
|
"{\n"
|
|
"201809051 => PG_VERSION_11,\n"
|
|
"},\n"
|
|
"};\n"
|
|
"\n\n\n\n"
|
|
"sub new\n"
|
|
"{\n"
|
|
"my $class = shift;\n"
|
|
"\n\n"
|
|
"my $self = {};\n"
|
|
"bless $self, $class;\n"
|
|
"\n\n"
|
|
"(\n"
|
|
"my $strOperation,\n"
|
|
"$self->{iRemoteIdx},\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->new', \\@_,\n"
|
|
"{name => 'iRemoteIdx', required => false},\n"
|
|
");\n"
|
|
"\n"
|
|
"if (defined($self->{iRemoteIdx}))\n"
|
|
"{\n"
|
|
"$self->{strDbPath} = cfgOption(cfgOptionIdFromIndex(CFGOPT_PG_PATH, $self->{iRemoteIdx}));\n"
|
|
"\n"
|
|
"if (!isDbLocal({iRemoteIdx => $self->{iRemoteIdx}}))\n"
|
|
"{\n"
|
|
"$self->{oProtocol} = protocolGet(CFGOPTVAL_REMOTE_TYPE_DB, $self->{iRemoteIdx});\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'self', value => $self}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub DESTROY\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my ($strOperation) = logDebugParam(__PACKAGE__ . '->DESTROY');\n"
|
|
"\n"
|
|
"if (defined($self->{hDb}))\n"
|
|
"{\n"
|
|
"$self->{hDb}->disconnect();\n"
|
|
"undef($self->{hDb});\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"return logDebugReturn($strOperation);\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub connect\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$bWarnOnError,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '::connect', \\@_,\n"
|
|
"{name => 'bWarnOnError', default => false},\n"
|
|
");\n"
|
|
"\n\n"
|
|
"my $bResult = true;\n"
|
|
"\n\n"
|
|
"if (defined($self->{oProtocol}))\n"
|
|
"{\n"
|
|
"\n"
|
|
"$bResult = $self->{oProtocol}->cmdExecute(OP_DB_CONNECT, undef, false, $bWarnOnError) ? true : false;\n"
|
|
"}\n"
|
|
"\n"
|
|
"else\n"
|
|
"{\n"
|
|
"if (!defined($self->{hDb}))\n"
|
|
"{\n"
|
|
"\n"
|
|
"my $strDbName = 'postgres';\n"
|
|
"my $strDbUser = getpwuid($<);\n"
|
|
"my $strDbSocketPath = cfgOption(cfgOptionIdFromIndex(CFGOPT_PG_SOCKET_PATH, $self->{iRemoteIdx}), false);\n"
|
|
"\n\n"
|
|
"if (defined($strDbSocketPath) && $strDbSocketPath !~ /^\\//)\n"
|
|
"{\n"
|
|
"confess &log(ERROR, \"'${strDbSocketPath}' is not valid for '\" . cfgOptionName(CFGOPT_PG_SOCKET_PATH) . \"' option:\" .\n"
|
|
"\" path must be absolute\", ERROR_OPTION_INVALID_VALUE);\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"my $strDbUri =\n"
|
|
"\"dbi:Pg:dbname=${strDbName};port=\" . cfgOption(cfgOptionIdFromIndex(CFGOPT_PG_PORT, $self->{iRemoteIdx})) .\n"
|
|
"(defined($strDbSocketPath) ? \";host=${strDbSocketPath}\" : '');\n"
|
|
"\n"
|
|
"logDebugMisc\n"
|
|
"(\n"
|
|
"$strOperation, undef,\n"
|
|
"{name => 'strDbUri', value => $strDbUri},\n"
|
|
"{name => 'strDbUser', value => $strDbUser}\n"
|
|
");\n"
|
|
"\n"
|
|
"$self->{hDb} = DBI->connect($strDbUri, $strDbUser, undef,\n"
|
|
"{AutoCommit => 1, RaiseError => 0, PrintError => 0, Warn => 0});\n"
|
|
"\n\n"
|
|
"if (!$self->{hDb})\n"
|
|
"{\n"
|
|
"\n"
|
|
"if (!$bWarnOnError)\n"
|
|
"{\n"
|
|
"confess &log(ERROR, $DBI::errstr, ERROR_DB_CONNECT);\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"&log(WARN, $DBI::errstr);\n"
|
|
"\n"
|
|
"$bResult = false;\n"
|
|
"undef($self->{hDb});\n"
|
|
"}\n"
|
|
"else\n"
|
|
"{\n"
|
|
"my ($fDbVersion) = $self->versionGet();\n"
|
|
"\n"
|
|
"if ($fDbVersion >= PG_VERSION_APPLICATION_NAME)\n"
|
|
"{\n"
|
|
"\n"
|
|
"$self->{hDb}->do(\n"
|
|
"\"set application_name = '\" . PROJECT_NAME . ' [' .\n"
|
|
"(cfgOptionValid(CFGOPT_COMMAND) ? cfgOption(CFGOPT_COMMAND) : cfgCommandName(cfgCommandGet())) . \"]'\")\n"
|
|
"or confess &log(ERROR, $self->{hDb}->errstr, ERROR_DB_QUERY);\n"
|
|
"\n\n"
|
|
"$self->{hDb}->do(\"set search_path = 'pg_catalog'\")\n"
|
|
"or confess &log(ERROR, $self->{hDb}->errstr, ERROR_DB_QUERY);\n"
|
|
"}\n"
|
|
"}\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'bResult', value => $bResult}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub executeSql\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$strSql,\n"
|
|
"$bIgnoreError,\n"
|
|
"$bResult,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '::executeSql', \\@_,\n"
|
|
"{name => 'strSql'},\n"
|
|
"{name => 'bIgnoreError', default => false},\n"
|
|
"{name => 'bResult', default => true},\n"
|
|
");\n"
|
|
"\n\n"
|
|
"my @stryResult;\n"
|
|
"\n\n"
|
|
"if (defined($self->{oProtocol}))\n"
|
|
"{\n"
|
|
"\n"
|
|
"@stryResult = @{$self->{oProtocol}->cmdExecute(OP_DB_EXECUTE_SQL, [$strSql, $bIgnoreError, $bResult], $bResult)};\n"
|
|
"}\n"
|
|
"\n"
|
|
"else\n"
|
|
"{\n"
|
|
"$self->connect();\n"
|
|
"\n\n"
|
|
"my $hStatement = $self->{hDb}->prepare($strSql, {pg_async => PG_ASYNC})\n"
|
|
"or confess &log(ERROR, $DBI::errstr . \":\\n${strSql}\", ERROR_DB_QUERY);\n"
|
|
"\n\n"
|
|
"$hStatement->execute()\n"
|
|
"or confess &log(ERROR, $DBI::errstr. \":\\n${strSql}\", ERROR_DB_QUERY);\n"
|
|
"\n\n"
|
|
"my $oWait = waitInit(cfgOption(CFGOPT_DB_TIMEOUT));\n"
|
|
"my $bTimeout = true;\n"
|
|
"\n"
|
|
"do\n"
|
|
"{\n"
|
|
"\n"
|
|
"if ($hStatement->pg_ready())\n"
|
|
"{\n"
|
|
"\n"
|
|
"if (!$bResult)\n"
|
|
"{\n"
|
|
"return \\@stryResult;\n"
|
|
"}\n"
|
|
"\n"
|
|
"if (!$hStatement->pg_result())\n"
|
|
"{\n"
|
|
"\n"
|
|
"if ($bIgnoreError)\n"
|
|
"{\n"
|
|
"return \\@stryResult;\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"confess &log(ERROR, $DBI::errstr . \":\\n${strSql}\", ERROR_DB_QUERY);\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"my @stryRow;\n"
|
|
"\n"
|
|
"do\n"
|
|
"{\n"
|
|
"\n"
|
|
"@stryRow = $hStatement->fetchrow_array;\n"
|
|
"\n\n"
|
|
"if (@stryRow)\n"
|
|
"{\n"
|
|
"push(@{$stryResult[@stryResult]}, @stryRow);\n"
|
|
"}\n"
|
|
"\n"
|
|
"elsif ($hStatement->err)\n"
|
|
"{\n"
|
|
"confess &log(ERROR, $DBI::errstr . \":\\n${strSql}\", ERROR_DB_QUERY);\n"
|
|
"}\n"
|
|
"}\n"
|
|
"while (@stryRow);\n"
|
|
"\n"
|
|
"$bTimeout = false;\n"
|
|
"}\n"
|
|
"} while ($bTimeout && waitMore($oWait));\n"
|
|
"\n\n"
|
|
"if ($bTimeout)\n"
|
|
"{\n"
|
|
"$hStatement->pg_cancel();\n"
|
|
"confess &log(ERROR, 'statement timed out after ' . waitInterval($oWait) .\n"
|
|
"\" second(s):\\n${strSql}\", ERROR_DB_TIMEOUT);\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'stryResult', value => \\@stryResult, ref => true}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub executeSqlRow\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$strSql\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->executeSqlRow', \\@_,\n"
|
|
"{name => 'strSql'}\n"
|
|
");\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'stryResult', value => @{$self->executeSql($strSql)}[0]}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub executeSqlOne\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$strSql\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->executeSqlOne', \\@_,\n"
|
|
"{name => 'strSql'}\n"
|
|
");\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'strResult', value => @{@{$self->executeSql($strSql)}[0]}[0]}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n\n\n"
|
|
"sub tablespaceMapGet\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my ($strOperation) = logDebugParam(__PACKAGE__ . '->tablespaceMapGet');\n"
|
|
"\n"
|
|
"my $hTablespaceMap = {};\n"
|
|
"\n"
|
|
"for my $strRow (@{$self->executeSql('select oid, spcname from pg_tablespace')})\n"
|
|
"{\n"
|
|
"$hTablespaceMap->{@{$strRow}[0]} = @{$strRow}[1];\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'hTablespaceMap', value => $hTablespaceMap}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n\n\n"
|
|
"sub databaseMapGet\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my ($strOperation) = logDebugParam(__PACKAGE__ . '->databaseMapGet');\n"
|
|
"\n"
|
|
"my $hDatabaseMap = {};\n"
|
|
"\n"
|
|
"for my $strRow (@{$self->executeSql('select datname, oid, datlastsysoid from pg_database')})\n"
|
|
"{\n"
|
|
"$hDatabaseMap->{@{$strRow}[0]}{&MANIFEST_KEY_DB_ID} = @{$strRow}[1];\n"
|
|
"$hDatabaseMap->{@{$strRow}[0]}{&MANIFEST_KEY_DB_LAST_SYSTEM_ID} = @{$strRow}[2];\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'hDatabaseMap', value => $hDatabaseMap}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub info\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$strDbPath\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->info', \\@_,\n"
|
|
"{name => 'strDbPath', default => $self->{strDbPath}}\n"
|
|
");\n"
|
|
"\n\n\n"
|
|
"if (!defined($self->{info}{$strDbPath}))\n"
|
|
"{\n"
|
|
"\n\n"
|
|
"if (defined($self->{oProtocol}))\n"
|
|
"{\n"
|
|
"\n"
|
|
"($self->{info}{$strDbPath}{strDbVersion}, $self->{info}{$strDbPath}{iDbControlVersion},\n"
|
|
"$self->{info}{$strDbPath}{iDbCatalogVersion}, $self->{info}{$strDbPath}{ullDbSysId}) =\n"
|
|
"$self->{oProtocol}->cmdExecute(OP_DB_INFO, [$strDbPath], true);\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"else\n"
|
|
"{\n"
|
|
"\n\n"
|
|
"my $strControlFile = \"${strDbPath}/\" . DB_FILE_PGCONTROL;\n"
|
|
"my $hFile;\n"
|
|
"my $tBlock;\n"
|
|
"\n"
|
|
"sysopen($hFile, $strControlFile, O_RDONLY)\n"
|
|
"or confess &log(ERROR, \"unable to open ${strControlFile}\", ERROR_FILE_OPEN);\n"
|
|
"\n\n"
|
|
"sysread($hFile, $tBlock, 8) == 8\n"
|
|
"or confess &log(ERROR, \"unable to read database system identifier\");\n"
|
|
"\n"
|
|
"$self->{info}{$strDbPath}{ullDbSysId} = unpack('Q', $tBlock);\n"
|
|
"\n\n"
|
|
"sysread($hFile, $tBlock, 4) == 4\n"
|
|
"or confess &log(ERROR, \"unable to read control version\");\n"
|
|
"\n"
|
|
"$self->{info}{$strDbPath}{iDbControlVersion} = unpack('L', $tBlock);\n"
|
|
"\n\n"
|
|
"sysread($hFile, $tBlock, 4) == 4\n"
|
|
"or confess &log(ERROR, \"unable to read catalog version\");\n"
|
|
"\n"
|
|
"$self->{info}{$strDbPath}{iDbCatalogVersion} = unpack('L', $tBlock);\n"
|
|
"\n\n"
|
|
"close($hFile);\n"
|
|
"\n\n"
|
|
"$self->{info}{$strDbPath}{strDbVersion} =\n"
|
|
"$oPgControlVersionHash->{$self->{info}{$strDbPath}{iDbControlVersion}}\n"
|
|
"{$self->{info}{$strDbPath}{iDbCatalogVersion}};\n"
|
|
"\n"
|
|
"if (!defined($self->{info}{$strDbPath}{strDbVersion}))\n"
|
|
"{\n"
|
|
"confess &log(\n"
|
|
"ERROR,\n"
|
|
"'unexpected control version = ' . $self->{info}{$strDbPath}{iDbControlVersion} .\n"
|
|
"' and catalog version = ' . $self->{info}{$strDbPath}{iDbCatalogVersion} . \"\\n\" .\n"
|
|
"'HINT: is this version of PostgreSQL supported?',\n"
|
|
"ERROR_VERSION_NOT_SUPPORTED);\n"
|
|
"}\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'strDbVersion', value => $self->{info}{$strDbPath}{strDbVersion}},\n"
|
|
"{name => 'iDbControlVersion', value => $self->{info}{$strDbPath}{iDbControlVersion}},\n"
|
|
"{name => 'iDbCatalogVersion', value => $self->{info}{$strDbPath}{iDbCatalogVersion}},\n"
|
|
"{name => 'ullDbSysId', value => $self->{info}{$strDbPath}{ullDbSysId}}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub versionGet\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my ($strOperation) = logDebugParam(__PACKAGE__ . '->versionGet');\n"
|
|
"\n\n"
|
|
"if (defined($self->{strDbVersion}) && defined($self->{strDbPath}))\n"
|
|
"{\n"
|
|
"return $self->{strDbVersion}, $self->{strDbPath};\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"my ($strVersionNum, $strDbPath) =\n"
|
|
"$self->executeSqlRow(\n"
|
|
"\"select (select setting from pg_settings where name = 'server_version_num'), \" .\n"
|
|
"\" (select setting from pg_settings where name = 'data_directory')\");\n"
|
|
"\n\n"
|
|
"$self->{strDbVersion} = substr($strVersionNum, 0, length($strVersionNum) - 4);\n"
|
|
"\n\n"
|
|
"if ($self->{strDbVersion} < PG_VERSION_10)\n"
|
|
"{\n"
|
|
"$self->{strDbVersion} .= qw{.} . int(substr($strVersionNum, 1, 2));\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"my @stryVersionSupport = versionSupport();\n"
|
|
"\n"
|
|
"if ($self->{strDbVersion} < $stryVersionSupport[0])\n"
|
|
"{\n"
|
|
"confess &log(ERROR, 'unsupported Postgres version' . $self->{strDbVersion}, ERROR_VERSION_NOT_SUPPORTED);\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'strDbVersion', value => $self->{strDbVersion}},\n"
|
|
"{name => 'strDbPath', value => $strDbPath}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub backupStart\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$strLabel,\n"
|
|
"$bStartFast\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->backupStart', \\@_,\n"
|
|
"{name => 'strLabel'},\n"
|
|
"{name => 'bStartFast'}\n"
|
|
");\n"
|
|
"\n\n"
|
|
"$self->configValidate();\n"
|
|
"\n\n"
|
|
"if ($self->{strDbVersion} < PG_VERSION_84 && $bStartFast)\n"
|
|
"{\n"
|
|
"&log(WARN, cfgOptionName(CFGOPT_START_FAST) . ' option is only available in PostgreSQL >= ' . PG_VERSION_84);\n"
|
|
"$bStartFast = false;\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"my $bChecksumPage =\n"
|
|
"$self->executeSqlOne(\"select count(*) = 1 from pg_settings where name = 'data_checksums' and setting = 'on'\");\n"
|
|
"\n\n"
|
|
"if (!cfgOptionTest(CFGOPT_CHECKSUM_PAGE))\n"
|
|
"{\n"
|
|
"cfgOptionSet(CFGOPT_CHECKSUM_PAGE, $bChecksumPage);\n"
|
|
"}\n"
|
|
"\n"
|
|
"elsif (cfgOption(CFGOPT_CHECKSUM_PAGE) && !$bChecksumPage)\n"
|
|
"{\n"
|
|
"&log(WARN, 'unable to enable page checksums since they are not enabled in the database');\n"
|
|
"cfgOptionSet(CFGOPT_CHECKSUM_PAGE, false);\n"
|
|
"}\n"
|
|
"\n\n\n"
|
|
"if (!$self->executeSqlOne('select pg_try_advisory_lock(' . DB_BACKUP_ADVISORY_LOCK . ')'))\n"
|
|
"{\n"
|
|
"confess &log(ERROR, 'unable to acquire ' . PROJECT_NAME . \" advisory lock\\n\" .\n"
|
|
"'HINT: is another ' . PROJECT_NAME . ' backup already running on this cluster?', ERROR_LOCK_ACQUIRE);\n"
|
|
"}\n"
|
|
"\n\n\n"
|
|
"if (cfgOption(CFGOPT_STOP_AUTO) && $self->{strDbVersion} < PG_VERSION_96)\n"
|
|
"{\n"
|
|
"\n"
|
|
"if ($self->{strDbVersion} >= PG_VERSION_93)\n"
|
|
"{\n"
|
|
"\n"
|
|
"if ($self->executeSqlOne('select pg_is_in_backup()'))\n"
|
|
"{\n"
|
|
"&log(WARN, 'the cluster is already in backup mode but no ' . PROJECT_NAME . ' backup process is running.' .\n"
|
|
"' pg_stop_backup() will be called so a new backup can be started.');\n"
|
|
"$self->backupStop();\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"else\n"
|
|
"{\n"
|
|
"&log(WARN, cfgOptionName(CFGOPT_STOP_AUTO) . ' option is only available in PostgreSQL >= ' . PG_VERSION_93);\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"&log(INFO, 'execute ' . ($self->{strDbVersion} >= PG_VERSION_96 ? 'non-' : '') .\n"
|
|
"\"exclusive pg_start_backup() with label \\\"${strLabel}\\\": backup begins after \" .\n"
|
|
"($bStartFast ? \"the requested immediate checkpoint\" : \"the next regular checkpoint\") . \" completes\");\n"
|
|
"\n"
|
|
"my ($strTimestampDbStart, $strArchiveStart, $strLsnStart, $iWalSegmentSize) = $self->executeSqlRow(\n"
|
|
"\"select to_char(current_timestamp, 'YYYY-MM-DD HH24:MI:SS.US TZ'), pg_\" . $self->walId() . \"file_name(lsn), lsn::text,\" .\n"
|
|
"($self->{strDbVersion} < PG_VERSION_84 ? PG_WAL_SIZE_83 :\n"
|
|
"\" (select setting::int8 from pg_settings where name = 'wal_segment_size')\" .\n"
|
|
"\n"
|
|
"($self->{strDbVersion} < PG_VERSION_11 ?\n"
|
|
"\" * (select setting::int8 from pg_settings where name = 'wal_block_size')\" : '')) .\n"
|
|
"\" from pg_start_backup('${strLabel}'\" .\n"
|
|
"($bStartFast ? ', true' : $self->{strDbVersion} >= PG_VERSION_84 ? ', false' : '') .\n"
|
|
"($self->{strDbVersion} >= PG_VERSION_96 ? ', false' : '') . ') as lsn');\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'strArchiveStart', value => $strArchiveStart},\n"
|
|
"{name => 'strLsnStart', value => $strLsnStart},\n"
|
|
"{name => 'iWalSegmentSize', value => $iWalSegmentSize},\n"
|
|
"{name => 'strTimestampDbStart', value => $strTimestampDbStart}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub backupStop\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my ($strOperation) = logDebugParam(__PACKAGE__ . '->backupStop');\n"
|
|
"\n\n"
|
|
"&log(INFO, 'execute ' . ($self->{strDbVersion} >= PG_VERSION_96 ? 'non-' : '') .\n"
|
|
"'exclusive pg_stop_backup() and wait for all WAL segments to archive');\n"
|
|
"\n"
|
|
"my ($strTimestampDbStop, $strArchiveStop, $strLsnStop, $strLabel, $strTablespaceMap) =\n"
|
|
"$self->executeSqlRow(\n"
|
|
"\"select to_char(clock_timestamp(), 'YYYY-MM-DD HH24:MI:SS.US TZ'), pg_\" .\n"
|
|
"$self->walId() . \"file_name(lsn), lsn::text, \" .\n"
|
|
"($self->{strDbVersion} >= PG_VERSION_96 ?\n"
|
|
"'labelfile, ' .\n"
|
|
"'case when length(trim(both \\'\\t\\n \\' from spcmapfile)) = 0 then null else spcmapfile end as spcmapfile' :\n"
|
|
"'null as labelfile, null as spcmapfile') .\n"
|
|
"' from pg_stop_backup(' .\n"
|
|
"\n"
|
|
"($self->{strDbVersion} >= PG_VERSION_96 ? 'false' : '') .\n"
|
|
"\n"
|
|
"($self->{strDbVersion} >= PG_VERSION_10 ? ', false' : '') . ') as lsn');\n"
|
|
"\n\n"
|
|
"my $oFileHash =\n"
|
|
"{\n"
|
|
"&MANIFEST_FILE_BACKUPLABEL => $strLabel,\n"
|
|
"&MANIFEST_FILE_TABLESPACEMAP => $strTablespaceMap\n"
|
|
"};\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'strArchiveStop', value => $strArchiveStop},\n"
|
|
"{name => 'strLsnStop', value => $strLsnStop},\n"
|
|
"{name => 'strTimestampDbStop', value => $strTimestampDbStop},\n"
|
|
"{name => 'oFileHash', value => $oFileHash}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n\n\n"
|
|
"sub configValidate\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->configValidate', \\@_,\n"
|
|
");\n"
|
|
"\n\n"
|
|
"my ($strDbVersion) = $self->info();\n"
|
|
"\n\n"
|
|
"my ($fCompareDbVersion, $strCompareDbPath) = $self->versionGet();\n"
|
|
"\n\n"
|
|
"if (!($strDbVersion == $fCompareDbVersion && $self->{strDbPath} eq $strCompareDbPath))\n"
|
|
"{\n"
|
|
"confess &log(ERROR,\n"
|
|
"\"version '${fCompareDbVersion}' and path '${strCompareDbPath}' queried from cluster do not match version\" .\n"
|
|
"\" '${strDbVersion}' and \" . cfgOptionName(CFGOPT_PG_PATH) . \" '$self->{strDbPath}' read from\" .\n"
|
|
"\" '$self->{strDbPath}/\" . DB_FILE_PGCONTROL . \"'\\n\" .\n"
|
|
"\"HINT: the \" . cfgOptionName(CFGOPT_PG_PATH) . \" and \" . cfgOptionName(CFGOPT_PG_PORT) .\n"
|
|
"\" settings likely reference different clusters\",\n"
|
|
"ERROR_DB_MISMATCH);\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if (!$self->isStandby() && cfgOptionValid(CFGOPT_ARCHIVE_CHECK) && cfgOption(CFGOPT_ARCHIVE_CHECK))\n"
|
|
"{\n"
|
|
"my $strArchiveMode = $self->executeSqlOne('show archive_mode');\n"
|
|
"\n\n"
|
|
"if ($strArchiveMode eq 'off')\n"
|
|
"{\n"
|
|
"confess &log(ERROR, 'archive_mode must be enabled', ERROR_ARCHIVE_DISABLED);\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if ($strArchiveMode eq 'always')\n"
|
|
"{\n"
|
|
"confess &log(ERROR, \"archive_mode=always not supported\", ERROR_FEATURE_NOT_SUPPORTED);\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"my $strArchiveCommand = $self->executeSqlOne('show archive_command');\n"
|
|
"\n"
|
|
"if (index($strArchiveCommand, PROJECT_EXE) == -1)\n"
|
|
"{\n"
|
|
"confess &log(ERROR,\n"
|
|
"'archive_command ' . (defined($strArchiveCommand) ? \"'${strArchiveCommand}'\" : '[null]') . ' must contain \\'' .\n"
|
|
"PROJECT_EXE . '\\'', ERROR_ARCHIVE_COMMAND_INVALID);\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n\n\n"
|
|
"sub walId\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n"
|
|
"return $self->{strDbVersion} >= PG_VERSION_10 ? 'wal' : 'xlog';\n"
|
|
"}\n"
|
|
"\n\n\n\n\n\n"
|
|
"sub lsnId\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n"
|
|
"return $self->{strDbVersion} >= PG_VERSION_10 ? 'lsn' : 'location';\n"
|
|
"}\n"
|
|
"\n\n\n\n\n\n"
|
|
"sub walSwitch\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my $strOperation = logDebugParam(__PACKAGE__ . '->walSwitch');\n"
|
|
"\n\n\n"
|
|
"if ($self->{strDbVersion} >= PG_VERSION_91)\n"
|
|
"{\n"
|
|
"$self->executeSql(\"select pg_create_restore_point('\" . PROJECT_NAME . \" Archive Check');\");\n"
|
|
"}\n"
|
|
"\n"
|
|
"my $strWalFileName = $self->executeSqlOne(\n"
|
|
"'select pg_' . $self->walId() . 'file_name from pg_' . $self->walId() . 'file_name(pg_switch_' . $self->walId() . '());');\n"
|
|
"\n"
|
|
"&log(INFO, \"switch WAL ${strWalFileName}\");\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'strWalFileName', value => $strWalFileName}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n\n\n"
|
|
"sub isStandby\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my ($strOperation) = logDebugParam(__PACKAGE__ . '->isStandby');\n"
|
|
"\n"
|
|
"if (!defined($self->{bStandby}))\n"
|
|
"{\n"
|
|
"my ($strDbVersion) = $self->versionGet();\n"
|
|
"\n"
|
|
"if ($strDbVersion <= PG_VERSION_90)\n"
|
|
"{\n"
|
|
"$self->{bStandby} = false;\n"
|
|
"}\n"
|
|
"else\n"
|
|
"{\n"
|
|
"$self->{bStandby} = $self->executeSqlOne('select pg_is_in_recovery()') ? true : false;\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'bStandby', value => $self->{bStandby}}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n\n\n"
|
|
"sub replayWait\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$strTargetLSN,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->replayWait', \\@_,\n"
|
|
"{name => 'strTargetLSN'}\n"
|
|
");\n"
|
|
"\n\n"
|
|
"require pgBackRest::Archive::Common;\n"
|
|
"pgBackRest::Archive::Common->import();\n"
|
|
"\n\n"
|
|
"my $oWait = waitInit(cfgOption(CFGOPT_ARCHIVE_TIMEOUT));\n"
|
|
"my $bTimeout = true;\n"
|
|
"my $strReplayedLSN = undef;\n"
|
|
"\n\n"
|
|
"do\n"
|
|
"{\n"
|
|
"my $strLastWalReplayLsnFunction =\n"
|
|
"'pg_last_' . $self->walId() . '_replay_' . $self->lsnId() . '()';\n"
|
|
"\n\n"
|
|
"my $strLastReplayedLSN = $self->executeSqlOne(\n"
|
|
"\"select coalesce(${strLastWalReplayLsnFunction}::text, '<NONE>')\");\n"
|
|
"\n\n"
|
|
"if ($strLastReplayedLSN eq '<NONE>')\n"
|
|
"{\n"
|
|
"confess &log(\n"
|
|
"ERROR,\n"
|
|
"\"unable to query replay lsn on the standby using ${strLastWalReplayLsnFunction}\\n\" .\n"
|
|
"\"Hint: Is this a standby?\",\n"
|
|
"ERROR_ARCHIVE_TIMEOUT);\n"
|
|
"}\n"
|
|
"\n\n\n"
|
|
"if (lsnNormalize($strLastReplayedLSN) ge lsnNormalize($strTargetLSN))\n"
|
|
"{\n"
|
|
"$bTimeout = false;\n"
|
|
"}\n"
|
|
"else\n"
|
|
"{\n"
|
|
"\n"
|
|
"if (defined($strReplayedLSN) &&\n"
|
|
"lsnNormalize($strLastReplayedLSN) gt lsnNormalize($strReplayedLSN) &&\n"
|
|
"!waitMore($oWait))\n"
|
|
"{\n"
|
|
"$oWait = waitInit(cfgOption(CFGOPT_ARCHIVE_TIMEOUT));\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"$strReplayedLSN = $strLastReplayedLSN;\n"
|
|
"\n"
|
|
"} while ($bTimeout && waitMore($oWait));\n"
|
|
"\n\n"
|
|
"if ($bTimeout == true)\n"
|
|
"{\n"
|
|
"confess &log(\n"
|
|
"ERROR, \"timeout before standby replayed ${strTargetLSN} - only reached ${strReplayedLSN}\", ERROR_ARCHIVE_TIMEOUT);\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"$self->executeSql('checkpoint', undef, false);\n"
|
|
"\n\n\n\n\n\n"
|
|
"my $strCheckpointLSN = undef;\n"
|
|
"\n"
|
|
"if ($self->{strDbVersion} >= PG_VERSION_96)\n"
|
|
"{\n"
|
|
"$strCheckpointLSN = $self->executeSqlOne('select checkpoint_' . $self->lsnId() .' from pg_control_checkpoint()');\n"
|
|
"\n"
|
|
"if (lsnNormalize($strCheckpointLSN) le lsnNormalize($strTargetLSN))\n"
|
|
"{\n"
|
|
"confess &log(\n"
|
|
"ERROR,\n"
|
|
"\"the checkpoint location ${strCheckpointLSN} is less than the target location ${strTargetLSN} even though the\" .\n"
|
|
"\" replay location is ${strReplayedLSN}\\n\" .\n"
|
|
"\"Hint: This should not be possible and may indicate a bug in PostgreSQL.\",\n"
|
|
"ERROR_ARCHIVE_TIMEOUT);\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'strReplayedLSN', value => $strReplayedLSN},\n"
|
|
"{name => 'strCheckpointLSN', value => $strCheckpointLSN},\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n\n\n\n\n"
|
|
"sub dbObjectGet\n"
|
|
"{\n"
|
|
"\n"
|
|
"my (\n"
|
|
"$strOperation,\n"
|
|
"$bMasterOnly,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '::dbObjectGet', \\@_,\n"
|
|
"{name => 'bMasterOnly', optional => true, default => false},\n"
|
|
");\n"
|
|
"\n"
|
|
"my $iStandbyIdx = undef;\n"
|
|
"my $iMasterRemoteIdx = 1;\n"
|
|
"my $oDbMaster = undef;\n"
|
|
"my $oDbStandby = undef;\n"
|
|
"\n\n\n"
|
|
"if (!$bMasterOnly && cfgOptionTest(CFGOPT_ONLINE) && cfgOption(CFGOPT_ONLINE) && multipleDb())\n"
|
|
"{\n"
|
|
"for (my $iRemoteIdx = 1; $iRemoteIdx <= cfgOptionIndexTotal(CFGOPT_PG_HOST); $iRemoteIdx++)\n"
|
|
"{\n"
|
|
"\n"
|
|
"if (cfgOptionTest(cfgOptionIdFromIndex(CFGOPT_PG_PATH, $iRemoteIdx)) ||\n"
|
|
"cfgOptionTest(cfgOptionIdFromIndex(CFGOPT_PG_HOST, $iRemoteIdx)))\n"
|
|
"{\n"
|
|
"\n"
|
|
"my $oDb;\n"
|
|
"\n"
|
|
"logWarnOnErrorEnable();\n"
|
|
"eval\n"
|
|
"{\n"
|
|
"$oDb = new pgBackRest::Db($iRemoteIdx);\n"
|
|
"return true;\n"
|
|
"}\n"
|
|
"or do {};\n"
|
|
"\n"
|
|
"logWarnOnErrorDisable();\n"
|
|
"my $bAssigned = false;\n"
|
|
"\n"
|
|
"if (defined($oDb))\n"
|
|
"{\n"
|
|
"\n\n"
|
|
"if ($oDb->connect(true))\n"
|
|
"{\n"
|
|
"\n"
|
|
"if ($oDb->isStandby())\n"
|
|
"{\n"
|
|
"\n"
|
|
"if (cfgOption(CFGOPT_BACKUP_STANDBY) && !defined($oDbStandby))\n"
|
|
"{\n"
|
|
"$oDbStandby = $oDb;\n"
|
|
"$iStandbyIdx = $iRemoteIdx;\n"
|
|
"$bAssigned = true;\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n"
|
|
"else\n"
|
|
"{\n"
|
|
"\n"
|
|
"if (defined($oDbMaster))\n"
|
|
"{\n"
|
|
"confess &log(ERROR, 'more than one master database found');\n"
|
|
"}\n"
|
|
"\n"
|
|
"$oDbMaster = $oDb;\n"
|
|
"$iMasterRemoteIdx = $iRemoteIdx;\n"
|
|
"$bAssigned = true;\n"
|
|
"}\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if (!$bAssigned)\n"
|
|
"{\n"
|
|
"protocolDestroy(CFGOPTVAL_REMOTE_TYPE_DB, $iRemoteIdx, true);\n"
|
|
"}\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if (cfgOption(CFGOPT_BACKUP_STANDBY) && !defined($oDbStandby))\n"
|
|
"{\n"
|
|
"\n"
|
|
"confess &log(ERROR, 'unable to find standby database - cannot proceed', ERROR_HOST_CONNECT);\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if (!defined($oDbMaster))\n"
|
|
"{\n"
|
|
"\n"
|
|
"confess &log(ERROR, 'unable to find master database - cannot proceed', ERROR_DB_CONNECT);\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if (!defined($oDbMaster))\n"
|
|
"{\n"
|
|
"$oDbMaster = new pgBackRest::Db($iMasterRemoteIdx);\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'oDbMaster', value => $oDbMaster},\n"
|
|
"{name => 'iDbMasterIdx', value => $iMasterRemoteIdx},\n"
|
|
"{name => 'oDbStandby', value => $oDbStandby},\n"
|
|
"{name => 'iDbStandbyIdx', value => $iStandbyIdx},\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n"
|
|
"push @EXPORT, qw(dbObjectGet);\n"
|
|
"\n\n\n\n\n\n\n"
|
|
"sub dbMasterGet\n"
|
|
"{\n"
|
|
"\n"
|
|
"my ($strOperation) = logDebugParam(__PACKAGE__ . '::dbMasterGet');\n"
|
|
"\n"
|
|
"my ($oDbMaster) = dbObjectGet({bMasterOnly => true});\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'oDbMaster', value => $oDbMaster, trace => true},\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n"
|
|
"push @EXPORT, qw(dbMasterGet);\n"
|
|
"\n\n\n\n\n\n"
|
|
"sub multipleDb\n"
|
|
"{\n"
|
|
"for (my $iDbPathIdx = 2; $iDbPathIdx <= cfgOptionIndexTotal(CFGOPT_PG_PATH); $iDbPathIdx++)\n"
|
|
"{\n"
|
|
"\n"
|
|
"if (cfgOptionTest(cfgOptionIdFromIndex(CFGOPT_PG_PATH, $iDbPathIdx)))\n"
|
|
"{\n"
|
|
"return true;\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n"
|
|
"return false;\n"
|
|
"}\n"
|
|
"\n"
|
|
"1;\n"
|
|
},
|
|
{
|
|
.name = "pgBackRest/DbVersion.pm",
|
|
.data =
|
|
"\n\n\n"
|
|
"package pgBackRest::DbVersion;\n"
|
|
"\n"
|
|
"use strict;\n"
|
|
"use warnings FATAL => qw(all);\n"
|
|
"use Carp qw(confess);\n"
|
|
"\n"
|
|
"use Exporter qw(import);\n"
|
|
"our @EXPORT = qw();\n"
|
|
"\n"
|
|
"use pgBackRest::Common::Log;\n"
|
|
"\n\n\n\n"
|
|
"use constant PG_PAGE_SIZE => 8192;\n"
|
|
"push @EXPORT, qw(PG_PAGE_SIZE);\n"
|
|
"\n\n\n\n"
|
|
"use constant PG_VERSION_83 => '8.3';\n"
|
|
"push @EXPORT, qw(PG_VERSION_83);\n"
|
|
"use constant PG_VERSION_84 => '8.4';\n"
|
|
"push @EXPORT, qw(PG_VERSION_84);\n"
|
|
"use constant PG_VERSION_90 => '9.0';\n"
|
|
"push @EXPORT, qw(PG_VERSION_90);\n"
|
|
"use constant PG_VERSION_91 => '9.1';\n"
|
|
"push @EXPORT, qw(PG_VERSION_91);\n"
|
|
"use constant PG_VERSION_92 => '9.2';\n"
|
|
"push @EXPORT, qw(PG_VERSION_92);\n"
|
|
"use constant PG_VERSION_93 => '9.3';\n"
|
|
"push @EXPORT, qw(PG_VERSION_93);\n"
|
|
"use constant PG_VERSION_94 => '9.4';\n"
|
|
"push @EXPORT, qw(PG_VERSION_94);\n"
|
|
"use constant PG_VERSION_95 => '9.5';\n"
|
|
"push @EXPORT, qw(PG_VERSION_95);\n"
|
|
"use constant PG_VERSION_96 => '9.6';\n"
|
|
"push @EXPORT, qw(PG_VERSION_96);\n"
|
|
"use constant PG_VERSION_10 => '10';\n"
|
|
"push @EXPORT, qw(PG_VERSION_10);\n"
|
|
"use constant PG_VERSION_11 => '11';\n"
|
|
"push @EXPORT, qw(PG_VERSION_11);\n"
|
|
"\n"
|
|
"use constant PG_VERSION_APPLICATION_NAME => PG_VERSION_90;\n"
|
|
"push @EXPORT, qw(PG_VERSION_APPLICATION_NAME);\n"
|
|
"use constant PG_VERSION_HOT_STANDBY => PG_VERSION_91;\n"
|
|
"push @EXPORT, qw(PG_VERSION_HOT_STANDBY);\n"
|
|
"use constant PG_VERSION_BACKUP_STANDBY => PG_VERSION_92;\n"
|
|
"push @EXPORT, qw(PG_VERSION_BACKUP_STANDBY);\n"
|
|
"\n\n\n\n\n\n"
|
|
"sub versionSupport\n"
|
|
"{\n"
|
|
"\n"
|
|
"my ($strOperation) = logDebugParam(__PACKAGE__ . '->versionSupport');\n"
|
|
"\n"
|
|
"my @strySupportVersion = (PG_VERSION_83, PG_VERSION_84, PG_VERSION_90, PG_VERSION_91, PG_VERSION_92, PG_VERSION_93,\n"
|
|
"PG_VERSION_94, PG_VERSION_95, PG_VERSION_96, PG_VERSION_10, PG_VERSION_11);\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'strySupportVersion', value => \\@strySupportVersion}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n"
|
|
"push @EXPORT, qw(versionSupport);\n"
|
|
"\n"
|
|
"1;\n"
|
|
},
|
|
{
|
|
.name = "pgBackRest/Expire.pm",
|
|
.data =
|
|
"\n\n\n"
|
|
"package pgBackRest::Expire;\n"
|
|
"\n"
|
|
"use strict;\n"
|
|
"use warnings FATAL => qw(all);\n"
|
|
"use Carp qw(confess);\n"
|
|
"\n"
|
|
"use Exporter qw(import);\n"
|
|
"use File::Basename qw(dirname);\n"
|
|
"use Scalar::Util qw(looks_like_number);\n"
|
|
"\n"
|
|
"use pgBackRest::Archive::Common;\n"
|
|
"use pgBackRest::Archive::Info;\n"
|
|
"use pgBackRest::Common::Exception;\n"
|
|
"use pgBackRest::Common::Ini;\n"
|
|
"use pgBackRest::Common::Log;\n"
|
|
"use pgBackRest::Backup::Common;\n"
|
|
"use pgBackRest::Backup::Info;\n"
|
|
"use pgBackRest::Config::Config;\n"
|
|
"use pgBackRest::InfoCommon;\n"
|
|
"use pgBackRest::Manifest;\n"
|
|
"use pgBackRest::Protocol::Helper;\n"
|
|
"use pgBackRest::Protocol::Storage::Helper;\n"
|
|
"\n\n\n\n"
|
|
"sub new\n"
|
|
"{\n"
|
|
"my $class = shift;\n"
|
|
"\n\n"
|
|
"my $self = {};\n"
|
|
"bless $self, $class;\n"
|
|
"\n\n"
|
|
"my ($strOperation) = logDebugParam(__PACKAGE__ . '->new');\n"
|
|
"\n\n"
|
|
"$self->{iArchiveExpireTotal} = 0;\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'self', value => $self}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n\n\n"
|
|
"sub logExpire\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"my $strArchiveId = shift;\n"
|
|
"my $strArchiveFile = shift;\n"
|
|
"\n"
|
|
"if (defined($strArchiveFile))\n"
|
|
"{\n"
|
|
"if (!defined($self->{strArchiveExpireStart}))\n"
|
|
"{\n"
|
|
"$self->{strArchiveExpireStart} = $strArchiveFile;\n"
|
|
"$self->{strArchiveExpireStop} = $strArchiveFile;\n"
|
|
"}\n"
|
|
"else\n"
|
|
"{\n"
|
|
"$self->{strArchiveExpireStop} = $strArchiveFile;\n"
|
|
"}\n"
|
|
"\n"
|
|
"$self->{iArchiveExpireTotal}++;\n"
|
|
"}\n"
|
|
"else\n"
|
|
"{\n"
|
|
"if (defined($self->{strArchiveExpireStart}))\n"
|
|
"{\n"
|
|
"&log(DETAIL, \"remove archive: archiveId = ${strArchiveId}, start = \" . substr($self->{strArchiveExpireStart}, 0, 24) .\n"
|
|
"\", stop = \" . substr($self->{strArchiveExpireStop}, 0, 24));\n"
|
|
"}\n"
|
|
"\n"
|
|
"undef($self->{strArchiveExpireStart});\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n\n\n\n\n\n"
|
|
"sub process\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my ($strOperation) = logDebugParam(__PACKAGE__ . '->process');\n"
|
|
"\n"
|
|
"my @stryPath;\n"
|
|
"\n"
|
|
"my $oStorageRepo = storageRepo();\n"
|
|
"my $strBackupClusterPath = $oStorageRepo->pathGet(STORAGE_REPO_BACKUP);\n"
|
|
"my $iFullRetention = cfgOption(CFGOPT_REPO_RETENTION_FULL, false);\n"
|
|
"my $iDifferentialRetention = cfgOption(CFGOPT_REPO_RETENTION_DIFF, false);\n"
|
|
"my $strArchiveRetentionType = cfgOption(CFGOPT_REPO_RETENTION_ARCHIVE_TYPE, false);\n"
|
|
"my $iArchiveRetention = cfgOption(CFGOPT_REPO_RETENTION_ARCHIVE, false);\n"
|
|
"\n\n"
|
|
"my $oBackupInfo = new pgBackRest::Backup::Info($oStorageRepo->pathGet(STORAGE_REPO_BACKUP));\n"
|
|
"\n\n"
|
|
"if (defined($iFullRetention))\n"
|
|
"{\n"
|
|
"\n"
|
|
"if (!looks_like_number($iFullRetention) || $iFullRetention < 1)\n"
|
|
"{\n"
|
|
"confess &log(ERROR, cfgOptionName(CFGOPT_REPO_RETENTION_FULL) . ' must be a number >= 1');\n"
|
|
"}\n"
|
|
"\n"
|
|
"@stryPath = $oBackupInfo->list(backupRegExpGet(true));\n"
|
|
"\n"
|
|
"if (@stryPath > $iFullRetention)\n"
|
|
"{\n"
|
|
"\n"
|
|
"for (my $iFullIdx = 0; $iFullIdx < @stryPath - $iFullRetention; $iFullIdx++)\n"
|
|
"{\n"
|
|
"my @stryRemoveList;\n"
|
|
"\n"
|
|
"foreach my $strPath ($oBackupInfo->list('^' . $stryPath[$iFullIdx] . '.*'))\n"
|
|
"{\n"
|
|
"$oStorageRepo->remove(STORAGE_REPO_BACKUP . \"/${strPath}/\" . FILE_MANIFEST . INI_COPY_EXT);\n"
|
|
"$oStorageRepo->remove(STORAGE_REPO_BACKUP . \"/${strPath}/\" . FILE_MANIFEST);\n"
|
|
"$oBackupInfo->delete($strPath);\n"
|
|
"\n"
|
|
"if ($strPath ne $stryPath[$iFullIdx])\n"
|
|
"{\n"
|
|
"push(@stryRemoveList, $strPath);\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n"
|
|
"&log(INFO, 'expire full backup ' . (@stryRemoveList > 0 ? 'set: ' : '') . $stryPath[$iFullIdx] .\n"
|
|
"(@stryRemoveList > 0 ? ', ' . join(', ', @stryRemoveList) : ''));\n"
|
|
"}\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if (defined($iDifferentialRetention))\n"
|
|
"{\n"
|
|
"\n"
|
|
"if (!looks_like_number($iDifferentialRetention) || $iDifferentialRetention < 1)\n"
|
|
"{\n"
|
|
"confess &log(ERROR, cfgOptionName(CFGOPT_REPO_RETENTION_DIFF) . ' must be a number >= 1');\n"
|
|
"}\n"
|
|
"\n\n\n"
|
|
"@stryPath = $oBackupInfo->list(backupRegExpGet(true, true));\n"
|
|
"\n"
|
|
"if (@stryPath > $iDifferentialRetention)\n"
|
|
"{\n"
|
|
"for (my $iDiffIdx = 0; $iDiffIdx < @stryPath - $iDifferentialRetention; $iDiffIdx++)\n"
|
|
"{\n"
|
|
"\n\n"
|
|
"next if ($stryPath[$iDiffIdx] =~ backupRegExpGet(true));\n"
|
|
"\n\n"
|
|
"my @stryRemoveList;\n"
|
|
"\n"
|
|
"foreach my $strPath ($oBackupInfo->list(backupRegExpGet(false, true, true)))\n"
|
|
"{\n"
|
|
"logDebugMisc($strOperation, \"checking ${strPath} for differential expiration\");\n"
|
|
"\n\n"
|
|
"if ($strPath lt $stryPath[$iDiffIdx + 1])\n"
|
|
"{\n"
|
|
"$oStorageRepo->remove(STORAGE_REPO_BACKUP . \"/${strPath}\" . FILE_MANIFEST);\n"
|
|
"$oBackupInfo->delete($strPath);\n"
|
|
"\n"
|
|
"if ($strPath ne $stryPath[$iDiffIdx])\n"
|
|
"{\n"
|
|
"push(@stryRemoveList, $strPath);\n"
|
|
"}\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n"
|
|
"&log(INFO, 'expire diff backup ' . (@stryRemoveList > 0 ? 'set: ' : '') . $stryPath[$iDiffIdx] .\n"
|
|
"(@stryRemoveList > 0 ? ', ' . join(', ', @stryRemoveList) : ''));\n"
|
|
"}\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n"
|
|
"$oBackupInfo->save();\n"
|
|
"\n\n"
|
|
"foreach my $strBackup ($oStorageRepo->list(\n"
|
|
"STORAGE_REPO_BACKUP, {strExpression => backupRegExpGet(true, true, true), strSortOrder => 'reverse'}))\n"
|
|
"{\n"
|
|
"if (!$oBackupInfo->current($strBackup))\n"
|
|
"{\n"
|
|
"&log(INFO, \"remove expired backup ${strBackup}\");\n"
|
|
"\n"
|
|
"$oStorageRepo->remove(\"${strBackupClusterPath}/${strBackup}\", {bRecurse => true});\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if (!defined($iArchiveRetention))\n"
|
|
"{\n"
|
|
"&log(INFO, \"option '\" . cfgOptionName(CFGOPT_REPO_RETENTION_ARCHIVE) . \"' is not set - archive logs will not be expired\");\n"
|
|
"}\n"
|
|
"else\n"
|
|
"{\n"
|
|
"my @stryGlobalBackupRetention;\n"
|
|
"\n\n\n"
|
|
"if ($strArchiveRetentionType eq CFGOPTVAL_BACKUP_TYPE_FULL)\n"
|
|
"{\n"
|
|
"@stryGlobalBackupRetention = $oBackupInfo->list(backupRegExpGet(true), 'reverse');\n"
|
|
"}\n"
|
|
"elsif ($strArchiveRetentionType eq CFGOPTVAL_BACKUP_TYPE_DIFF)\n"
|
|
"{\n"
|
|
"@stryGlobalBackupRetention = $oBackupInfo->list(backupRegExpGet(true, true), 'reverse');\n"
|
|
"}\n"
|
|
"elsif ($strArchiveRetentionType eq CFGOPTVAL_BACKUP_TYPE_INCR)\n"
|
|
"{\n"
|
|
"@stryGlobalBackupRetention = $oBackupInfo->list(backupRegExpGet(true, true, true), 'reverse');\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"my $iBackupTotal = scalar @stryGlobalBackupRetention;\n"
|
|
"\n"
|
|
"if ($iBackupTotal > 0)\n"
|
|
"{\n"
|
|
"my $oArchiveInfo = new pgBackRest::Archive::Info($oStorageRepo->pathGet(STORAGE_REPO_ARCHIVE), true);\n"
|
|
"my @stryListArchiveDisk = sort {((split('-', $a))[1] + 0) cmp ((split('-', $b))[1] + 0)} $oStorageRepo->list(\n"
|
|
"STORAGE_REPO_ARCHIVE, {strExpression => REGEX_ARCHIVE_DIR_DB_VERSION, bIgnoreMissing => true});\n"
|
|
"\n\n"
|
|
"if (!($oArchiveInfo->test(INFO_ARCHIVE_SECTION_DB, INFO_ARCHIVE_KEY_DB_VERSION, undef,\n"
|
|
"($oBackupInfo->get(INFO_BACKUP_SECTION_DB, INFO_BACKUP_KEY_DB_VERSION)))) ||\n"
|
|
"!($oArchiveInfo->test(INFO_ARCHIVE_SECTION_DB, INFO_ARCHIVE_KEY_DB_SYSTEM_ID, undef,\n"
|
|
"($oBackupInfo->get(INFO_BACKUP_SECTION_DB, INFO_BACKUP_KEY_SYSTEM_ID)))))\n"
|
|
"{\n"
|
|
"confess &log(ERROR, \"archive and backup database versions do not match\\n\" .\n"
|
|
"\"HINT: has a stanza-upgrade been performed?\", ERROR_FILE_INVALID);\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"my @stryTmp = @stryGlobalBackupRetention;\n"
|
|
"my @stryGlobalBackupArchiveRetention = splice(@stryTmp, 0, $iArchiveRetention);\n"
|
|
"\n\n"
|
|
"foreach my $strArchiveId (@stryListArchiveDisk)\n"
|
|
"{\n"
|
|
"\n\n"
|
|
"my @stryLocalBackupRetention = $oBackupInfo->listByArchiveId($strArchiveId,\n"
|
|
"$oStorageRepo->pathGet(STORAGE_REPO_ARCHIVE), \\@stryGlobalBackupRetention, 'reverse');\n"
|
|
"\n\n"
|
|
"if (!@stryLocalBackupRetention)\n"
|
|
"{\n"
|
|
"\n"
|
|
"my $iDbHistoryId = $oBackupInfo->backupArchiveDbHistoryId(\n"
|
|
"$strArchiveId, $oStorageRepo->pathGet(STORAGE_REPO_ARCHIVE));\n"
|
|
"\n\n\n"
|
|
"if (!defined($iDbHistoryId) || !$oBackupInfo->test(INFO_BACKUP_SECTION_DB, INFO_BACKUP_KEY_HISTORY_ID, undef,\n"
|
|
"$iDbHistoryId))\n"
|
|
"{\n"
|
|
"my $strFullPath = $oStorageRepo->pathGet(STORAGE_REPO_ARCHIVE . \"/${strArchiveId}\");\n"
|
|
"\n"
|
|
"$oStorageRepo->remove($strFullPath, {bRecurse => true});\n"
|
|
"\n"
|
|
"&log(INFO, \"remove archive path: ${strFullPath}\");\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"next;\n"
|
|
"}\n"
|
|
"\n"
|
|
"my @stryLocalBackupArchiveRentention;\n"
|
|
"\n\n"
|
|
"if (@stryGlobalBackupArchiveRetention && $iArchiveRetention <= scalar @stryGlobalBackupRetention)\n"
|
|
"{\n"
|
|
"\n"
|
|
"foreach my $strGlobalBackupArchiveRetention (@stryGlobalBackupArchiveRetention)\n"
|
|
"{\n"
|
|
"foreach my $strLocalBackupRetention (@stryLocalBackupRetention)\n"
|
|
"{\n"
|
|
"if ($strLocalBackupRetention eq $strGlobalBackupArchiveRetention)\n"
|
|
"{\n"
|
|
"unshift(@stryLocalBackupArchiveRentention, $strLocalBackupRetention);\n"
|
|
"}\n"
|
|
"}\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n\n"
|
|
"else\n"
|
|
"{\n"
|
|
"if ($strArchiveRetentionType eq CFGOPTVAL_BACKUP_TYPE_FULL && scalar @stryLocalBackupRetention > 0)\n"
|
|
"{\n"
|
|
"&log(INFO, \"full backup total < ${iArchiveRetention} - using oldest full backup for ${strArchiveId} \" .\n"
|
|
"\"archive retention\");\n"
|
|
"$stryLocalBackupArchiveRentention[0] = $stryLocalBackupRetention[0];\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n\n"
|
|
"if (!@stryLocalBackupArchiveRentention)\n"
|
|
"{\n"
|
|
"$stryLocalBackupArchiveRentention[0] = $stryLocalBackupRetention[-1];\n"
|
|
"}\n"
|
|
"\n"
|
|
"my $strArchiveRetentionBackup = $stryLocalBackupArchiveRentention[0];\n"
|
|
"\n\n"
|
|
"if (defined($strArchiveRetentionBackup))\n"
|
|
"{\n"
|
|
"my $bRemove;\n"
|
|
"\n\n\n"
|
|
"if ($oBackupInfo->test(INFO_BACKUP_SECTION_BACKUP_CURRENT,\n"
|
|
"$strArchiveRetentionBackup, INFO_BACKUP_KEY_ARCHIVE_START))\n"
|
|
"{\n"
|
|
"\n\n\n"
|
|
"my $strArchiveExpireMax;\n"
|
|
"my @oyArchiveRange;\n"
|
|
"my @stryBackupList = $oBackupInfo->list();\n"
|
|
"\n\n"
|
|
"foreach my $strBackup (\n"
|
|
"$oBackupInfo->listByArchiveId(\n"
|
|
"$strArchiveId, $oStorageRepo->pathGet(STORAGE_REPO_ARCHIVE), \\@stryBackupList))\n"
|
|
"{\n"
|
|
"if ($strBackup le $strArchiveRetentionBackup &&\n"
|
|
"$oBackupInfo->test(INFO_BACKUP_SECTION_BACKUP_CURRENT, $strBackup, INFO_BACKUP_KEY_ARCHIVE_START))\n"
|
|
"{\n"
|
|
"my $oArchiveRange = {};\n"
|
|
"\n"
|
|
"$$oArchiveRange{start} = $oBackupInfo->get(INFO_BACKUP_SECTION_BACKUP_CURRENT,\n"
|
|
"$strBackup, INFO_BACKUP_KEY_ARCHIVE_START);\n"
|
|
"\n"
|
|
"if ($strBackup ne $strArchiveRetentionBackup)\n"
|
|
"{\n"
|
|
"$$oArchiveRange{stop} = $oBackupInfo->get(INFO_BACKUP_SECTION_BACKUP_CURRENT,\n"
|
|
"$strBackup, INFO_BACKUP_KEY_ARCHIVE_STOP);\n"
|
|
"}\n"
|
|
"else\n"
|
|
"{\n"
|
|
"$strArchiveExpireMax = $$oArchiveRange{start};\n"
|
|
"}\n"
|
|
"\n"
|
|
"&log(DETAIL, \"archive retention on backup ${strBackup}, archiveId = ${strArchiveId}, \" .\n"
|
|
"\"start = $$oArchiveRange{start}\" .\n"
|
|
"(defined($$oArchiveRange{stop}) ? \", stop = $$oArchiveRange{stop}\" : ''));\n"
|
|
"\n"
|
|
"push(@oyArchiveRange, $oArchiveRange);\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"foreach my $strPath ($oStorageRepo->list(\n"
|
|
"STORAGE_REPO_ARCHIVE . \"/${strArchiveId}\", {strExpression => REGEX_ARCHIVE_DIR_WAL}))\n"
|
|
"{\n"
|
|
"logDebugMisc($strOperation, \"found major WAL path: ${strPath}\");\n"
|
|
"$bRemove = true;\n"
|
|
"\n\n"
|
|
"foreach my $oArchiveRange (@oyArchiveRange)\n"
|
|
"{\n"
|
|
"if ($strPath ge substr($$oArchiveRange{start}, 0, 16) &&\n"
|
|
"(!defined($$oArchiveRange{stop}) || $strPath le substr($$oArchiveRange{stop}, 0, 16)))\n"
|
|
"{\n"
|
|
"$bRemove = false;\n"
|
|
"last;\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if ($bRemove)\n"
|
|
"{\n"
|
|
"my $strFullPath = $oStorageRepo->pathGet(STORAGE_REPO_ARCHIVE . \"/${strArchiveId}\") . \"/${strPath}\";\n"
|
|
"\n"
|
|
"$oStorageRepo->remove($strFullPath, {bRecurse => true});\n"
|
|
"\n\n"
|
|
"logDebugMisc($strOperation, \"remove major WAL path: ${strFullPath}\");\n"
|
|
"$self->logExpire($strArchiveId, $strPath);\n"
|
|
"}\n"
|
|
"\n\n\n"
|
|
"elsif ($strPath le substr($strArchiveExpireMax, 0, 16))\n"
|
|
"{\n"
|
|
"\n"
|
|
"foreach my $strSubPath ($oStorageRepo->list(\n"
|
|
"STORAGE_REPO_ARCHIVE . \"/${strArchiveId}/${strPath}\", {strExpression => \"^[0-F]{24}.*\\$\"}))\n"
|
|
"{\n"
|
|
"$bRemove = true;\n"
|
|
"\n\n"
|
|
"foreach my $oArchiveRange (@oyArchiveRange)\n"
|
|
"{\n"
|
|
"if (substr($strSubPath, 0, 24) ge $$oArchiveRange{start} &&\n"
|
|
"(!defined($$oArchiveRange{stop}) || substr($strSubPath, 0, 24) le $$oArchiveRange{stop}))\n"
|
|
"{\n"
|
|
"$bRemove = false;\n"
|
|
"last;\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if ($bRemove)\n"
|
|
"{\n"
|
|
"$oStorageRepo->remove(STORAGE_REPO_ARCHIVE . \"/${strArchiveId}/${strSubPath}\");\n"
|
|
"\n"
|
|
"logDebugMisc($strOperation, \"remove WAL segment: ${strArchiveId}/${strSubPath}\");\n"
|
|
"\n\n"
|
|
"$self->logExpire($strArchiveId, substr($strSubPath, 0, 24));\n"
|
|
"}\n"
|
|
"else\n"
|
|
"{\n"
|
|
"\n"
|
|
"$self->logExpire($strArchiveId);\n"
|
|
"}\n"
|
|
"}\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if ($self->{iArchiveExpireTotal} == 0)\n"
|
|
"{\n"
|
|
"&log(DETAIL, \"no archive to remove, archiveId = ${strArchiveId}\");\n"
|
|
"}\n"
|
|
"}\n"
|
|
"}\n"
|
|
"}\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"return logDebugReturn($strOperation);\n"
|
|
"}\n"
|
|
"\n"
|
|
"1;\n"
|
|
},
|
|
{
|
|
.name = "pgBackRest/InfoCommon.pm",
|
|
.data =
|
|
"\n\n\n\n"
|
|
"package pgBackRest::InfoCommon;\n"
|
|
"\n"
|
|
"use strict;\n"
|
|
"use warnings FATAL => qw(all);\n"
|
|
"use Carp qw(confess);\n"
|
|
"\n"
|
|
"use Exporter qw(import);\n"
|
|
"our @EXPORT = qw();\n"
|
|
"\n\n\n\n"
|
|
"use constant INFO_BACKUP_SECTION_DB => 'db';\n"
|
|
"push @EXPORT, qw(INFO_BACKUP_SECTION_DB);\n"
|
|
"use constant INFO_BACKUP_SECTION_DB_HISTORY => INFO_BACKUP_SECTION_DB . ':history';\n"
|
|
"push @EXPORT, qw(INFO_BACKUP_SECTION_DB_HISTORY);\n"
|
|
"\n\n\n\n"
|
|
"use constant INFO_HISTORY_ID => 'id';\n"
|
|
"push @EXPORT, qw(INFO_HISTORY_ID);\n"
|
|
"use constant INFO_DB_VERSION => 'version';\n"
|
|
"push @EXPORT, qw(INFO_DB_VERSION);\n"
|
|
"use constant INFO_SYSTEM_ID => 'system-id';\n"
|
|
"push @EXPORT, qw(INFO_SYSTEM_ID);\n"
|
|
"\n"
|
|
"1;\n"
|
|
},
|
|
{
|
|
.name = "pgBackRest/LibC.pm",
|
|
.data =
|
|
"\n\n\n"
|
|
"package pgBackRest::LibC;\n"
|
|
"use base 'Exporter';\n"
|
|
"\n"
|
|
"use 5.010001;\n"
|
|
"use strict;\n"
|
|
"use warnings;\n"
|
|
"use Carp;\n"
|
|
"\n"
|
|
"use pgBackRest::LibCAuto;\n"
|
|
"\n\n"
|
|
"my $rhConstant = pgBackRest::LibCAuto::libcAutoConstant();\n"
|
|
"\n"
|
|
"foreach my $strConstant (keys(%{$rhConstant}))\n"
|
|
"{\n"
|
|
"eval\n"
|
|
"\"use constant ${strConstant} => '\" . $rhConstant->{$strConstant} . \"'\";\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"our %EXPORT_TAGS = %{pgBackRest::LibCAuto::libcAutoExportTag()};\n"
|
|
"our @EXPORT_OK;\n"
|
|
"\n"
|
|
"foreach my $strSection (keys(%EXPORT_TAGS))\n"
|
|
"{\n"
|
|
"\n\n"
|
|
"my $strPrefixLast = 'XXXXXXXX';\n"
|
|
"my $iConstantIdx = 0;\n"
|
|
"\n"
|
|
"foreach my $strConstant (@{$EXPORT_TAGS{$strSection}})\n"
|
|
"{\n"
|
|
"my $strPrefix = ($strConstant =~ m/^[A-Z0-9]+/g)[0];\n"
|
|
"\n"
|
|
"if (defined($strPrefix))\n"
|
|
"{\n"
|
|
"if ($strPrefix ne $strPrefixLast)\n"
|
|
"{\n"
|
|
"$iConstantIdx = 0;\n"
|
|
"}\n"
|
|
"else\n"
|
|
"{\n"
|
|
"$iConstantIdx++;\n"
|
|
"}\n"
|
|
"\n"
|
|
"if ($strPrefix eq 'CFGCMD' || $strPrefix eq 'CFGOPT')\n"
|
|
"{\n"
|
|
"eval\n"
|
|
"\"use constant ${strConstant} => ${iConstantIdx}\";\n"
|
|
"}\n"
|
|
"\n"
|
|
"$strPrefixLast = $strPrefix;\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"push(@EXPORT_OK, @{$EXPORT_TAGS{$strSection}});\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"our @EXPORT = ();\n"
|
|
"\n"
|
|
"1;\n"
|
|
},
|
|
{
|
|
.name = "pgBackRest/LibCAuto.pm",
|
|
.data =
|
|
"\n\n\n"
|
|
"package pgBackRest::LibCAuto;\n"
|
|
"\n"
|
|
"use strict;\n"
|
|
"use warnings;\n"
|
|
"\n\n"
|
|
"sub libcAutoConstant\n"
|
|
"{\n"
|
|
"return\n"
|
|
"{\n"
|
|
"CFGOPTVAL_INFO_OUTPUT_TEXT => 'text',\n"
|
|
"CFGOPTVAL_INFO_OUTPUT_JSON => 'json',\n"
|
|
"\n"
|
|
"CFGOPTVAL_REPO_CIPHER_TYPE_NONE => 'none',\n"
|
|
"CFGOPTVAL_REPO_CIPHER_TYPE_AES_256_CBC => 'aes-256-cbc',\n"
|
|
"\n"
|
|
"CFGOPTVAL_REPO_RETENTION_ARCHIVE_TYPE_FULL => 'full',\n"
|
|
"CFGOPTVAL_REPO_RETENTION_ARCHIVE_TYPE_DIFF => 'diff',\n"
|
|
"CFGOPTVAL_REPO_RETENTION_ARCHIVE_TYPE_INCR => 'incr',\n"
|
|
"\n"
|
|
"CFGOPTVAL_REPO_TYPE_CIFS => 'cifs',\n"
|
|
"CFGOPTVAL_REPO_TYPE_POSIX => 'posix',\n"
|
|
"CFGOPTVAL_REPO_TYPE_S3 => 's3',\n"
|
|
"\n"
|
|
"CFGOPTVAL_RESTORE_TARGET_ACTION_PAUSE => 'pause',\n"
|
|
"CFGOPTVAL_RESTORE_TARGET_ACTION_PROMOTE => 'promote',\n"
|
|
"CFGOPTVAL_RESTORE_TARGET_ACTION_SHUTDOWN => 'shutdown',\n"
|
|
"\n"
|
|
"CFGOPTVAL_BACKUP_TYPE_FULL => 'full',\n"
|
|
"CFGOPTVAL_BACKUP_TYPE_DIFF => 'diff',\n"
|
|
"CFGOPTVAL_BACKUP_TYPE_INCR => 'incr',\n"
|
|
"\n"
|
|
"CFGOPTVAL_LOCAL_TYPE_DB => 'db',\n"
|
|
"CFGOPTVAL_LOCAL_TYPE_BACKUP => 'backup',\n"
|
|
"\n"
|
|
"CFGOPTVAL_REMOTE_TYPE_DB => 'db',\n"
|
|
"CFGOPTVAL_REMOTE_TYPE_BACKUP => 'backup',\n"
|
|
"\n"
|
|
"CFGOPTVAL_RESTORE_TYPE_NAME => 'name',\n"
|
|
"CFGOPTVAL_RESTORE_TYPE_TIME => 'time',\n"
|
|
"CFGOPTVAL_RESTORE_TYPE_XID => 'xid',\n"
|
|
"CFGOPTVAL_RESTORE_TYPE_PRESERVE => 'preserve',\n"
|
|
"CFGOPTVAL_RESTORE_TYPE_NONE => 'none',\n"
|
|
"CFGOPTVAL_RESTORE_TYPE_IMMEDIATE => 'immediate',\n"
|
|
"CFGOPTVAL_RESTORE_TYPE_DEFAULT => 'default',\n"
|
|
"\n"
|
|
"CFGDEF_TYPE_BOOLEAN => 0,\n"
|
|
"CFGDEF_TYPE_FLOAT => 1,\n"
|
|
"CFGDEF_TYPE_HASH => 2,\n"
|
|
"CFGDEF_TYPE_INTEGER => 3,\n"
|
|
"CFGDEF_TYPE_LIST => 4,\n"
|
|
"CFGDEF_TYPE_PATH => 5,\n"
|
|
"CFGDEF_TYPE_SIZE => 6,\n"
|
|
"CFGDEF_TYPE_STRING => 7,\n"
|
|
"\n"
|
|
"ENCODE_TYPE_BASE64 => 0,\n"
|
|
"\n"
|
|
"CIPHER_MODE_ENCRYPT => 0,\n"
|
|
"CIPHER_MODE_DECRYPT => 1,\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"sub libcAutoExportTag\n"
|
|
"{\n"
|
|
"return\n"
|
|
"{\n"
|
|
"checksum =>\n"
|
|
"[\n"
|
|
"'pageChecksum',\n"
|
|
"'pageChecksumBufferTest',\n"
|
|
"'pageChecksumTest',\n"
|
|
"],\n"
|
|
"\n"
|
|
"config =>\n"
|
|
"[\n"
|
|
"'CFGOPTVAL_INFO_OUTPUT_TEXT',\n"
|
|
"'CFGOPTVAL_INFO_OUTPUT_JSON',\n"
|
|
"'CFGOPTVAL_REPO_CIPHER_TYPE_NONE',\n"
|
|
"'CFGOPTVAL_REPO_CIPHER_TYPE_AES_256_CBC',\n"
|
|
"'CFGOPTVAL_REPO_RETENTION_ARCHIVE_TYPE_FULL',\n"
|
|
"'CFGOPTVAL_REPO_RETENTION_ARCHIVE_TYPE_DIFF',\n"
|
|
"'CFGOPTVAL_REPO_RETENTION_ARCHIVE_TYPE_INCR',\n"
|
|
"'CFGOPTVAL_REPO_TYPE_CIFS',\n"
|
|
"'CFGOPTVAL_REPO_TYPE_POSIX',\n"
|
|
"'CFGOPTVAL_REPO_TYPE_S3',\n"
|
|
"'CFGOPTVAL_RESTORE_TARGET_ACTION_PAUSE',\n"
|
|
"'CFGOPTVAL_RESTORE_TARGET_ACTION_PROMOTE',\n"
|
|
"'CFGOPTVAL_RESTORE_TARGET_ACTION_SHUTDOWN',\n"
|
|
"'CFGOPTVAL_BACKUP_TYPE_FULL',\n"
|
|
"'CFGOPTVAL_BACKUP_TYPE_DIFF',\n"
|
|
"'CFGOPTVAL_BACKUP_TYPE_INCR',\n"
|
|
"'CFGOPTVAL_LOCAL_TYPE_DB',\n"
|
|
"'CFGOPTVAL_LOCAL_TYPE_BACKUP',\n"
|
|
"'CFGOPTVAL_REMOTE_TYPE_DB',\n"
|
|
"'CFGOPTVAL_REMOTE_TYPE_BACKUP',\n"
|
|
"'CFGOPTVAL_RESTORE_TYPE_NAME',\n"
|
|
"'CFGOPTVAL_RESTORE_TYPE_TIME',\n"
|
|
"'CFGOPTVAL_RESTORE_TYPE_XID',\n"
|
|
"'CFGOPTVAL_RESTORE_TYPE_PRESERVE',\n"
|
|
"'CFGOPTVAL_RESTORE_TYPE_NONE',\n"
|
|
"'CFGOPTVAL_RESTORE_TYPE_IMMEDIATE',\n"
|
|
"'CFGOPTVAL_RESTORE_TYPE_DEFAULT',\n"
|
|
"'CFGCMD_ARCHIVE_GET',\n"
|
|
"'CFGCMD_ARCHIVE_GET_ASYNC',\n"
|
|
"'CFGCMD_ARCHIVE_PUSH',\n"
|
|
"'CFGCMD_ARCHIVE_PUSH_ASYNC',\n"
|
|
"'CFGCMD_BACKUP',\n"
|
|
"'CFGCMD_CHECK',\n"
|
|
"'CFGCMD_EXPIRE',\n"
|
|
"'CFGCMD_HELP',\n"
|
|
"'CFGCMD_INFO',\n"
|
|
"'CFGCMD_LOCAL',\n"
|
|
"'CFGCMD_REMOTE',\n"
|
|
"'CFGCMD_RESTORE',\n"
|
|
"'CFGCMD_STANZA_CREATE',\n"
|
|
"'CFGCMD_STANZA_DELETE',\n"
|
|
"'CFGCMD_STANZA_UPGRADE',\n"
|
|
"'CFGCMD_START',\n"
|
|
"'CFGCMD_STOP',\n"
|
|
"'CFGCMD_VERSION',\n"
|
|
"'CFGOPT_ARCHIVE_ASYNC',\n"
|
|
"'CFGOPT_ARCHIVE_CHECK',\n"
|
|
"'CFGOPT_ARCHIVE_COPY',\n"
|
|
"'CFGOPT_ARCHIVE_GET_QUEUE_MAX',\n"
|
|
"'CFGOPT_ARCHIVE_PUSH_QUEUE_MAX',\n"
|
|
"'CFGOPT_ARCHIVE_TIMEOUT',\n"
|
|
"'CFGOPT_BACKUP_STANDBY',\n"
|
|
"'CFGOPT_BUFFER_SIZE',\n"
|
|
"'CFGOPT_C',\n"
|
|
"'CFGOPT_CHECKSUM_PAGE',\n"
|
|
"'CFGOPT_CMD_SSH',\n"
|
|
"'CFGOPT_COMMAND',\n"
|
|
"'CFGOPT_COMPRESS',\n"
|
|
"'CFGOPT_COMPRESS_LEVEL',\n"
|
|
"'CFGOPT_COMPRESS_LEVEL_NETWORK',\n"
|
|
"'CFGOPT_CONFIG',\n"
|
|
"'CFGOPT_CONFIG_INCLUDE_PATH',\n"
|
|
"'CFGOPT_CONFIG_PATH',\n"
|
|
"'CFGOPT_DB_INCLUDE',\n"
|
|
"'CFGOPT_DB_TIMEOUT',\n"
|
|
"'CFGOPT_DELTA',\n"
|
|
"'CFGOPT_EXCLUDE',\n"
|
|
"'CFGOPT_FORCE',\n"
|
|
"'CFGOPT_HOST_ID',\n"
|
|
"'CFGOPT_LINK_ALL',\n"
|
|
"'CFGOPT_LINK_MAP',\n"
|
|
"'CFGOPT_LOCK_PATH',\n"
|
|
"'CFGOPT_LOG_LEVEL_CONSOLE',\n"
|
|
"'CFGOPT_LOG_LEVEL_FILE',\n"
|
|
"'CFGOPT_LOG_LEVEL_STDERR',\n"
|
|
"'CFGOPT_LOG_PATH',\n"
|
|
"'CFGOPT_LOG_SUBPROCESS',\n"
|
|
"'CFGOPT_LOG_TIMESTAMP',\n"
|
|
"'CFGOPT_MANIFEST_SAVE_THRESHOLD',\n"
|
|
"'CFGOPT_NEUTRAL_UMASK',\n"
|
|
"'CFGOPT_ONLINE',\n"
|
|
"'CFGOPT_OUTPUT',\n"
|
|
"'CFGOPT_PERL_OPTION',\n"
|
|
"'CFGOPT_PG_HOST',\n"
|
|
"'CFGOPT_PG_HOST2',\n"
|
|
"'CFGOPT_PG_HOST3',\n"
|
|
"'CFGOPT_PG_HOST4',\n"
|
|
"'CFGOPT_PG_HOST5',\n"
|
|
"'CFGOPT_PG_HOST6',\n"
|
|
"'CFGOPT_PG_HOST7',\n"
|
|
"'CFGOPT_PG_HOST8',\n"
|
|
"'CFGOPT_PG_HOST_CMD',\n"
|
|
"'CFGOPT_PG_HOST_CMD2',\n"
|
|
"'CFGOPT_PG_HOST_CMD3',\n"
|
|
"'CFGOPT_PG_HOST_CMD4',\n"
|
|
"'CFGOPT_PG_HOST_CMD5',\n"
|
|
"'CFGOPT_PG_HOST_CMD6',\n"
|
|
"'CFGOPT_PG_HOST_CMD7',\n"
|
|
"'CFGOPT_PG_HOST_CMD8',\n"
|
|
"'CFGOPT_PG_HOST_CONFIG',\n"
|
|
"'CFGOPT_PG_HOST_CONFIG2',\n"
|
|
"'CFGOPT_PG_HOST_CONFIG3',\n"
|
|
"'CFGOPT_PG_HOST_CONFIG4',\n"
|
|
"'CFGOPT_PG_HOST_CONFIG5',\n"
|
|
"'CFGOPT_PG_HOST_CONFIG6',\n"
|
|
"'CFGOPT_PG_HOST_CONFIG7',\n"
|
|
"'CFGOPT_PG_HOST_CONFIG8',\n"
|
|
"'CFGOPT_PG_HOST_CONFIG_INCLUDE_PATH',\n"
|
|
"'CFGOPT_PG_HOST_CONFIG_INCLUDE_PATH2',\n"
|
|
"'CFGOPT_PG_HOST_CONFIG_INCLUDE_PATH3',\n"
|
|
"'CFGOPT_PG_HOST_CONFIG_INCLUDE_PATH4',\n"
|
|
"'CFGOPT_PG_HOST_CONFIG_INCLUDE_PATH5',\n"
|
|
"'CFGOPT_PG_HOST_CONFIG_INCLUDE_PATH6',\n"
|
|
"'CFGOPT_PG_HOST_CONFIG_INCLUDE_PATH7',\n"
|
|
"'CFGOPT_PG_HOST_CONFIG_INCLUDE_PATH8',\n"
|
|
"'CFGOPT_PG_HOST_CONFIG_PATH',\n"
|
|
"'CFGOPT_PG_HOST_CONFIG_PATH2',\n"
|
|
"'CFGOPT_PG_HOST_CONFIG_PATH3',\n"
|
|
"'CFGOPT_PG_HOST_CONFIG_PATH4',\n"
|
|
"'CFGOPT_PG_HOST_CONFIG_PATH5',\n"
|
|
"'CFGOPT_PG_HOST_CONFIG_PATH6',\n"
|
|
"'CFGOPT_PG_HOST_CONFIG_PATH7',\n"
|
|
"'CFGOPT_PG_HOST_CONFIG_PATH8',\n"
|
|
"'CFGOPT_PG_HOST_PORT',\n"
|
|
"'CFGOPT_PG_HOST_PORT2',\n"
|
|
"'CFGOPT_PG_HOST_PORT3',\n"
|
|
"'CFGOPT_PG_HOST_PORT4',\n"
|
|
"'CFGOPT_PG_HOST_PORT5',\n"
|
|
"'CFGOPT_PG_HOST_PORT6',\n"
|
|
"'CFGOPT_PG_HOST_PORT7',\n"
|
|
"'CFGOPT_PG_HOST_PORT8',\n"
|
|
"'CFGOPT_PG_HOST_USER',\n"
|
|
"'CFGOPT_PG_HOST_USER2',\n"
|
|
"'CFGOPT_PG_HOST_USER3',\n"
|
|
"'CFGOPT_PG_HOST_USER4',\n"
|
|
"'CFGOPT_PG_HOST_USER5',\n"
|
|
"'CFGOPT_PG_HOST_USER6',\n"
|
|
"'CFGOPT_PG_HOST_USER7',\n"
|
|
"'CFGOPT_PG_HOST_USER8',\n"
|
|
"'CFGOPT_PG_PATH',\n"
|
|
"'CFGOPT_PG_PATH2',\n"
|
|
"'CFGOPT_PG_PATH3',\n"
|
|
"'CFGOPT_PG_PATH4',\n"
|
|
"'CFGOPT_PG_PATH5',\n"
|
|
"'CFGOPT_PG_PATH6',\n"
|
|
"'CFGOPT_PG_PATH7',\n"
|
|
"'CFGOPT_PG_PATH8',\n"
|
|
"'CFGOPT_PG_PORT',\n"
|
|
"'CFGOPT_PG_PORT2',\n"
|
|
"'CFGOPT_PG_PORT3',\n"
|
|
"'CFGOPT_PG_PORT4',\n"
|
|
"'CFGOPT_PG_PORT5',\n"
|
|
"'CFGOPT_PG_PORT6',\n"
|
|
"'CFGOPT_PG_PORT7',\n"
|
|
"'CFGOPT_PG_PORT8',\n"
|
|
"'CFGOPT_PG_SOCKET_PATH',\n"
|
|
"'CFGOPT_PG_SOCKET_PATH2',\n"
|
|
"'CFGOPT_PG_SOCKET_PATH3',\n"
|
|
"'CFGOPT_PG_SOCKET_PATH4',\n"
|
|
"'CFGOPT_PG_SOCKET_PATH5',\n"
|
|
"'CFGOPT_PG_SOCKET_PATH6',\n"
|
|
"'CFGOPT_PG_SOCKET_PATH7',\n"
|
|
"'CFGOPT_PG_SOCKET_PATH8',\n"
|
|
"'CFGOPT_PROCESS',\n"
|
|
"'CFGOPT_PROCESS_MAX',\n"
|
|
"'CFGOPT_PROTOCOL_TIMEOUT',\n"
|
|
"'CFGOPT_RECOVERY_OPTION',\n"
|
|
"'CFGOPT_REPO_CIPHER_PASS',\n"
|
|
"'CFGOPT_REPO_CIPHER_TYPE',\n"
|
|
"'CFGOPT_REPO_HARDLINK',\n"
|
|
"'CFGOPT_REPO_HOST',\n"
|
|
"'CFGOPT_REPO_HOST_CMD',\n"
|
|
"'CFGOPT_REPO_HOST_CONFIG',\n"
|
|
"'CFGOPT_REPO_HOST_CONFIG_INCLUDE_PATH',\n"
|
|
"'CFGOPT_REPO_HOST_CONFIG_PATH',\n"
|
|
"'CFGOPT_REPO_HOST_PORT',\n"
|
|
"'CFGOPT_REPO_HOST_USER',\n"
|
|
"'CFGOPT_REPO_PATH',\n"
|
|
"'CFGOPT_REPO_RETENTION_ARCHIVE',\n"
|
|
"'CFGOPT_REPO_RETENTION_ARCHIVE_TYPE',\n"
|
|
"'CFGOPT_REPO_RETENTION_DIFF',\n"
|
|
"'CFGOPT_REPO_RETENTION_FULL',\n"
|
|
"'CFGOPT_REPO_S3_BUCKET',\n"
|
|
"'CFGOPT_REPO_S3_CA_FILE',\n"
|
|
"'CFGOPT_REPO_S3_CA_PATH',\n"
|
|
"'CFGOPT_REPO_S3_ENDPOINT',\n"
|
|
"'CFGOPT_REPO_S3_HOST',\n"
|
|
"'CFGOPT_REPO_S3_KEY',\n"
|
|
"'CFGOPT_REPO_S3_KEY_SECRET',\n"
|
|
"'CFGOPT_REPO_S3_REGION',\n"
|
|
"'CFGOPT_REPO_S3_TOKEN',\n"
|
|
"'CFGOPT_REPO_S3_VERIFY_TLS',\n"
|
|
"'CFGOPT_REPO_TYPE',\n"
|
|
"'CFGOPT_RESUME',\n"
|
|
"'CFGOPT_SET',\n"
|
|
"'CFGOPT_SPOOL_PATH',\n"
|
|
"'CFGOPT_STANZA',\n"
|
|
"'CFGOPT_START_FAST',\n"
|
|
"'CFGOPT_STOP_AUTO',\n"
|
|
"'CFGOPT_TABLESPACE_MAP',\n"
|
|
"'CFGOPT_TABLESPACE_MAP_ALL',\n"
|
|
"'CFGOPT_TARGET',\n"
|
|
"'CFGOPT_TARGET_ACTION',\n"
|
|
"'CFGOPT_TARGET_EXCLUSIVE',\n"
|
|
"'CFGOPT_TARGET_TIMELINE',\n"
|
|
"'CFGOPT_TEST',\n"
|
|
"'CFGOPT_TEST_DELAY',\n"
|
|
"'CFGOPT_TEST_POINT',\n"
|
|
"'CFGOPT_TYPE',\n"
|
|
"'cfgCommandName',\n"
|
|
"'cfgOptionIndex',\n"
|
|
"'cfgOptionIndexTotal',\n"
|
|
"'cfgOptionName',\n"
|
|
"],\n"
|
|
"\n"
|
|
"configDefine =>\n"
|
|
"[\n"
|
|
"'CFGDEF_TYPE_BOOLEAN',\n"
|
|
"'CFGDEF_TYPE_FLOAT',\n"
|
|
"'CFGDEF_TYPE_HASH',\n"
|
|
"'CFGDEF_TYPE_INTEGER',\n"
|
|
"'CFGDEF_TYPE_LIST',\n"
|
|
"'CFGDEF_TYPE_PATH',\n"
|
|
"'CFGDEF_TYPE_SIZE',\n"
|
|
"'CFGDEF_TYPE_STRING',\n"
|
|
"'cfgCommandId',\n"
|
|
"'cfgDefOptionDefault',\n"
|
|
"'cfgDefOptionPrefix',\n"
|
|
"'cfgDefOptionSecure',\n"
|
|
"'cfgDefOptionType',\n"
|
|
"'cfgDefOptionValid',\n"
|
|
"'cfgOptionId',\n"
|
|
"'cfgOptionTotal',\n"
|
|
"],\n"
|
|
"\n"
|
|
"crypto =>\n"
|
|
"[\n"
|
|
"'CIPHER_MODE_ENCRYPT',\n"
|
|
"'CIPHER_MODE_DECRYPT',\n"
|
|
"'cryptoHashOne',\n"
|
|
"],\n"
|
|
"\n"
|
|
"debug =>\n"
|
|
"[\n"
|
|
"'libcUvSize',\n"
|
|
"],\n"
|
|
"\n"
|
|
"encode =>\n"
|
|
"[\n"
|
|
"'ENCODE_TYPE_BASE64',\n"
|
|
"'decodeToBin',\n"
|
|
"'encodeToStr',\n"
|
|
"],\n"
|
|
"\n"
|
|
"lock =>\n"
|
|
"[\n"
|
|
"'lockAcquire',\n"
|
|
"'lockRelease',\n"
|
|
"],\n"
|
|
"\n"
|
|
"random =>\n"
|
|
"[\n"
|
|
"'cryptoRandomBytes',\n"
|
|
"],\n"
|
|
"\n"
|
|
"storage =>\n"
|
|
"[\n"
|
|
"'storagePosixPathRemove',\n"
|
|
"],\n"
|
|
"\n"
|
|
"test =>\n"
|
|
"[\n"
|
|
"'cfgParseTest',\n"
|
|
"],\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n"
|
|
"1;\n"
|
|
},
|
|
{
|
|
.name = "pgBackRest/Main.pm",
|
|
.data =
|
|
"\n\n\n"
|
|
"package pgBackRest::Main;\n"
|
|
"\n"
|
|
"use strict;\n"
|
|
"use warnings FATAL => qw(all);\n"
|
|
"use Carp qw(confess);\n"
|
|
"use English '-no_match_vars';\n"
|
|
"\n\n"
|
|
"$SIG{__DIE__} = sub {Carp::confess @_};\n"
|
|
"\n"
|
|
"use File::Basename qw(dirname);\n"
|
|
"\n"
|
|
"use pgBackRest::Common::Exception;\n"
|
|
"use pgBackRest::Common::Lock;\n"
|
|
"use pgBackRest::Common::Log;\n"
|
|
"use pgBackRest::Config::Config;\n"
|
|
"use pgBackRest::Protocol::Helper;\n"
|
|
"use pgBackRest::Storage::Helper;\n"
|
|
"use pgBackRest::Version;\n"
|
|
"\n\n\n\n"
|
|
"my $strConfigJson;\n"
|
|
"my $strConfigBin;\n"
|
|
"my $bConfigLoaded = false;\n"
|
|
"\n"
|
|
"sub mainConfigSet\n"
|
|
"{\n"
|
|
"$strConfigBin = shift;\n"
|
|
"$strConfigJson = shift;\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub main\n"
|
|
"{\n"
|
|
"my $strCommand = shift;\n"
|
|
"my @stryCommandArg = @_;\n"
|
|
"\n\n\n"
|
|
"my $iResult = 0;\n"
|
|
"my $bErrorC = false;\n"
|
|
"my $strMessage = '';\n"
|
|
"\n"
|
|
"eval\n"
|
|
"{\n"
|
|
"\n\n"
|
|
"if (!$bConfigLoaded)\n"
|
|
"{\n"
|
|
"configLoad(undef, $strConfigBin, $strCommand, \\$strConfigJson);\n"
|
|
"\n\n"
|
|
"if (cfgOptionTest(CFGOPT_TEST) && cfgOption(CFGOPT_TEST))\n"
|
|
"{\n"
|
|
"testSet(cfgOption(CFGOPT_TEST), cfgOption(CFGOPT_TEST_DELAY), cfgOption(CFGOPT_TEST_POINT, false));\n"
|
|
"}\n"
|
|
"\n"
|
|
"$bConfigLoaded = true;\n"
|
|
"}\n"
|
|
"else\n"
|
|
"{\n"
|
|
"cfgCommandSet(cfgCommandId($strCommand));\n"
|
|
"}\n"
|
|
"\n\n\n"
|
|
"if (cfgCommandTest(CFGCMD_REMOTE))\n"
|
|
"{\n"
|
|
"\n"
|
|
"cfgOptionSet(CFGOPT_LOG_LEVEL_STDERR, PROTOCOL, true);\n"
|
|
"logLevelSet(cfgOption(CFGOPT_LOG_LEVEL_FILE), OFF, cfgOption(CFGOPT_LOG_LEVEL_STDERR));\n"
|
|
"\n"
|
|
"logFileSet(\n"
|
|
"storageLocal(),\n"
|
|
"cfgOption(CFGOPT_LOG_PATH) . '/' . (cfgOptionTest(CFGOPT_STANZA) ? cfgOption(CFGOPT_STANZA) : 'all') . '-' .\n"
|
|
"lc(cfgOption(CFGOPT_COMMAND)) . '-' . lc(cfgCommandName(cfgCommandGet())) . '-' .\n"
|
|
"sprintf(\"%03d\", cfgOption(CFGOPT_PROCESS)));\n"
|
|
"\n"
|
|
"if (cfgOptionTest(CFGOPT_TYPE, CFGOPTVAL_REMOTE_TYPE_BACKUP) &&\n"
|
|
"!cfgOptionTest(CFGOPT_REPO_TYPE, CFGOPTVAL_REPO_TYPE_S3) &&\n"
|
|
"!-e cfgOption(CFGOPT_REPO_PATH))\n"
|
|
"{\n"
|
|
"confess &log(ERROR,\n"
|
|
"cfgOptionName(CFGOPT_REPO_PATH) . ' \\'' . cfgOption(CFGOPT_REPO_PATH) . '\\' does not exist',\n"
|
|
"ERROR_PATH_MISSING);\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"require pgBackRest::Protocol::Remote::Minion;\n"
|
|
"pgBackRest::Protocol::Remote::Minion->import();\n"
|
|
"\n\n"
|
|
"my $oRemote = new pgBackRest::Protocol::Remote::Minion(\n"
|
|
"cfgOption(CFGOPT_BUFFER_SIZE), cfgOption(CFGOPT_PROTOCOL_TIMEOUT));\n"
|
|
"\n\n"
|
|
"$oRemote->process(\n"
|
|
"cfgOption(CFGOPT_LOCK_PATH), cfgOption(CFGOPT_COMMAND), cfgOption(CFGOPT_STANZA, false), cfgOption(CFGOPT_PROCESS));\n"
|
|
"}\n"
|
|
"\n\n\n"
|
|
"elsif (cfgCommandTest(CFGCMD_LOCAL))\n"
|
|
"{\n"
|
|
"\n"
|
|
"cfgOptionSet(CFGOPT_LOG_LEVEL_STDERR, PROTOCOL, true);\n"
|
|
"logLevelSet(cfgOption(CFGOPT_LOG_LEVEL_FILE), OFF, cfgOption(CFGOPT_LOG_LEVEL_STDERR));\n"
|
|
"\n"
|
|
"logFileSet(\n"
|
|
"storageLocal(),\n"
|
|
"cfgOption(CFGOPT_LOG_PATH) . '/' . cfgOption(CFGOPT_STANZA) . '-' . lc(cfgOption(CFGOPT_COMMAND)) . '-' .\n"
|
|
"lc(cfgCommandName(cfgCommandGet())) . '-' . sprintf(\"%03d\", cfgOption(CFGOPT_PROCESS)));\n"
|
|
"\n\n"
|
|
"require pgBackRest::Protocol::Local::Minion;\n"
|
|
"pgBackRest::Protocol::Local::Minion->import();\n"
|
|
"\n\n"
|
|
"my $oLocal = new pgBackRest::Protocol::Local::Minion();\n"
|
|
"\n\n"
|
|
"$oLocal->process();\n"
|
|
"}\n"
|
|
"\n\n\n"
|
|
"elsif (cfgCommandTest(CFGCMD_CHECK))\n"
|
|
"{\n"
|
|
"\n"
|
|
"require pgBackRest::Check::Check;\n"
|
|
"pgBackRest::Check::Check->import();\n"
|
|
"\n"
|
|
"$iResult = new pgBackRest::Check::Check()->process();\n"
|
|
"}\n"
|
|
"\n\n\n"
|
|
"elsif (cfgCommandTest(CFGCMD_START))\n"
|
|
"{\n"
|
|
"lockStart();\n"
|
|
"}\n"
|
|
"elsif (cfgCommandTest(CFGCMD_STOP))\n"
|
|
"{\n"
|
|
"lockStop();\n"
|
|
"}\n"
|
|
"else\n"
|
|
"{\n"
|
|
"\n"
|
|
"require pgBackRest::Protocol::Storage::Helper;\n"
|
|
"pgBackRest::Protocol::Storage::Helper->import();\n"
|
|
"\n"
|
|
"if (isRepoLocal() && !cfgOptionTest(CFGOPT_REPO_TYPE, CFGOPTVAL_REPO_TYPE_S3) && !storageRepo()->pathExists(''))\n"
|
|
"{\n"
|
|
"confess &log(ERROR,\n"
|
|
"cfgOptionName(CFGOPT_REPO_PATH) . ' \\'' . cfgOption(CFGOPT_REPO_PATH) . '\\' does not exist',\n"
|
|
"ERROR_PATH_MISSING);\n"
|
|
"}\n"
|
|
"\n\n\n"
|
|
"if (cfgCommandTest(CFGCMD_INFO))\n"
|
|
"{\n"
|
|
"\n"
|
|
"require pgBackRest::Info;\n"
|
|
"pgBackRest::Info->import();\n"
|
|
"\n"
|
|
"new pgBackRest::Info()->process();\n"
|
|
"}\n"
|
|
"else\n"
|
|
"{\n"
|
|
"logFileSet(\n"
|
|
"storageLocal(),\n"
|
|
"cfgOption(CFGOPT_LOG_PATH) . '/' . cfgOption(CFGOPT_STANZA) . '-' . lc(cfgCommandName(cfgCommandGet())));\n"
|
|
"\n\n\n"
|
|
"if (cfgCommandTest(CFGCMD_STANZA_DELETE))\n"
|
|
"{\n"
|
|
"\n"
|
|
"require pgBackRest::Stanza;\n"
|
|
"pgBackRest::Stanza->import();\n"
|
|
"\n"
|
|
"new pgBackRest::Stanza()->process();\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"elsif (cfgCommandTest(CFGCMD_RESTORE))\n"
|
|
"{\n"
|
|
"\n"
|
|
"if (!isDbLocal())\n"
|
|
"{\n"
|
|
"confess &log(ERROR,\n"
|
|
"cfgCommandName(cfgCommandGet()) . ' command must be run on the PostgreSQL host', ERROR_HOST_INVALID);\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"require pgBackRest::Restore;\n"
|
|
"pgBackRest::Restore->import();\n"
|
|
"\n\n"
|
|
"new pgBackRest::Restore()->process();\n"
|
|
"}\n"
|
|
"else\n"
|
|
"{\n"
|
|
"\n"
|
|
"lockStopTest();\n"
|
|
"\n\n"
|
|
"if (!isRepoLocal())\n"
|
|
"{\n"
|
|
"confess &log(ERROR,\n"
|
|
"cfgCommandName(cfgCommandGet()) . ' command must be run on the repository host', ERROR_HOST_INVALID);\n"
|
|
"}\n"
|
|
"\n\n\n"
|
|
"if (cfgCommandTest(CFGCMD_STANZA_CREATE) || cfgCommandTest(CFGCMD_STANZA_UPGRADE))\n"
|
|
"{\n"
|
|
"\n"
|
|
"require pgBackRest::Stanza;\n"
|
|
"pgBackRest::Stanza->import();\n"
|
|
"\n"
|
|
"$iResult = new pgBackRest::Stanza()->process();\n"
|
|
"}\n"
|
|
"\n\n\n"
|
|
"elsif (cfgCommandTest(CFGCMD_BACKUP))\n"
|
|
"{\n"
|
|
"\n"
|
|
"require pgBackRest::Backup::Backup;\n"
|
|
"pgBackRest::Backup::Backup->import();\n"
|
|
"\n"
|
|
"new pgBackRest::Backup::Backup()->process();\n"
|
|
"}\n"
|
|
"\n\n\n"
|
|
"elsif (cfgCommandTest(CFGCMD_EXPIRE))\n"
|
|
"{\n"
|
|
"\n"
|
|
"require pgBackRest::Expire;\n"
|
|
"pgBackRest::Expire->import();\n"
|
|
"\n"
|
|
"new pgBackRest::Expire()->process();\n"
|
|
"}\n"
|
|
"}\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n"
|
|
"return 1;\n"
|
|
"}\n"
|
|
"\n\n\n"
|
|
"or do\n"
|
|
"{\n"
|
|
"\n\n\n"
|
|
"my $oException = defined($EVAL_ERROR) && length($EVAL_ERROR) > 0 ? $EVAL_ERROR : logErrorLast();\n"
|
|
"\n\n"
|
|
"if (isException(\\$oException))\n"
|
|
"{\n"
|
|
"$iResult = $oException->code();\n"
|
|
"$bErrorC = $oException->errorC();\n"
|
|
"\n\n"
|
|
"if (!$bConfigLoaded && cfgOption(CFGOPT_ARCHIVE_ASYNC))\n"
|
|
"{\n"
|
|
"$strMessage = $oException->message();\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n"
|
|
"else\n"
|
|
"{\n"
|
|
"$iResult = ERROR_UNHANDLED;\n"
|
|
"$strMessage =\n"
|
|
"'process terminated due to an unhandled exception' .\n"
|
|
"(defined($oException) ? \":\\n${oException}\" : ': [exception not defined]');\n"
|
|
"}\n"
|
|
"};\n"
|
|
"\n\n"
|
|
"return $iResult, $bErrorC, $strMessage;\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub mainCleanup\n"
|
|
"{\n"
|
|
"\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$iExitCode,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '::mainCleanup', \\@_,\n"
|
|
"{name => 'iExitCode', required => false},\n"
|
|
");\n"
|
|
"\n\n"
|
|
"eval\n"
|
|
"{\n"
|
|
"protocolDestroy(undef, undef, defined($iExitCode) && ($iExitCode == 0 || $iExitCode == 1));\n"
|
|
"return true;\n"
|
|
"}\n"
|
|
"\n"
|
|
"or do {};\n"
|
|
"\n\n"
|
|
"eval\n"
|
|
"{\n"
|
|
"lockRelease(false);\n"
|
|
"return true;\n"
|
|
"}\n"
|
|
"\n"
|
|
"or do {};\n"
|
|
"\n\n"
|
|
"return logDebugReturn($strOperation);\n"
|
|
"}\n"
|
|
"\n"
|
|
"1;\n"
|
|
},
|
|
{
|
|
.name = "pgBackRest/Manifest.pm",
|
|
.data =
|
|
"\n\n\n"
|
|
"package pgBackRest::Manifest;\n"
|
|
"use parent 'pgBackRest::Common::Ini';\n"
|
|
"\n"
|
|
"use strict;\n"
|
|
"use warnings FATAL => qw(all);\n"
|
|
"use Carp qw(confess);\n"
|
|
"\n"
|
|
"use Exporter qw(import);\n"
|
|
"our @EXPORT = qw();\n"
|
|
"use File::Basename qw(dirname basename);\n"
|
|
"use Time::Local qw(timelocal);\n"
|
|
"\n"
|
|
"use pgBackRest::DbVersion;\n"
|
|
"use pgBackRest::Common::Exception;\n"
|
|
"use pgBackRest::Common::Ini;\n"
|
|
"use pgBackRest::Common::Log;\n"
|
|
"use pgBackRest::Common::Wait;\n"
|
|
"use pgBackRest::Config::Config;\n"
|
|
"use pgBackRest::Protocol::Helper;\n"
|
|
"use pgBackRest::Protocol::Storage::Helper;\n"
|
|
"use pgBackRest::Storage::Helper;\n"
|
|
"\n\n\n\n"
|
|
"use constant PATH_BACKUP_HISTORY => 'backup.history';\n"
|
|
"push @EXPORT, qw(PATH_BACKUP_HISTORY);\n"
|
|
"use constant FILE_MANIFEST => 'backup.manifest';\n"
|
|
"push @EXPORT, qw(FILE_MANIFEST);\n"
|
|
"use constant FILE_MANIFEST_COPY => FILE_MANIFEST . INI_COPY_EXT;\n"
|
|
"push @EXPORT, qw(FILE_MANIFEST_COPY);\n"
|
|
"\n\n\n\n"
|
|
"use constant MANIFEST_DEFAULT_MATCH_FACTOR => 0.1;\n"
|
|
"push @EXPORT, qw(MANIFEST_DEFAULT_MATCH_FACTOR);\n"
|
|
"\n\n\n\n"
|
|
"use constant MANIFEST_TARGET_PGDATA => 'pg_data';\n"
|
|
"push @EXPORT, qw(MANIFEST_TARGET_PGDATA);\n"
|
|
"use constant MANIFEST_TARGET_PGTBLSPC => 'pg_tblspc';\n"
|
|
"push @EXPORT, qw(MANIFEST_TARGET_PGTBLSPC);\n"
|
|
"\n"
|
|
"use constant MANIFEST_VALUE_PATH => 'path';\n"
|
|
"push @EXPORT, qw(MANIFEST_VALUE_PATH);\n"
|
|
"use constant MANIFEST_VALUE_LINK => 'link';\n"
|
|
"push @EXPORT, qw(MANIFEST_VALUE_LINK);\n"
|
|
"\n\n"
|
|
"use constant MANIFEST_SECTION_BACKUP => 'backup';\n"
|
|
"push @EXPORT, qw(MANIFEST_SECTION_BACKUP);\n"
|
|
"use constant MANIFEST_SECTION_BACKUP_DB => 'backup:db';\n"
|
|
"push @EXPORT, qw(MANIFEST_SECTION_BACKUP_DB);\n"
|
|
"use constant MANIFEST_SECTION_BACKUP_INFO => 'backup:info';\n"
|
|
"push @EXPORT, qw(MANIFEST_SECTION_BACKUP_INFO);\n"
|
|
"use constant MANIFEST_SECTION_BACKUP_OPTION => 'backup:option';\n"
|
|
"push @EXPORT, qw(MANIFEST_SECTION_BACKUP_OPTION);\n"
|
|
"use constant MANIFEST_SECTION_BACKUP_TARGET => 'backup:target';\n"
|
|
"push @EXPORT, qw(MANIFEST_SECTION_BACKUP_TARGET);\n"
|
|
"use constant MANIFEST_SECTION_DB => 'db';\n"
|
|
"push @EXPORT, qw(MANIFEST_SECTION_DB);\n"
|
|
"use constant MANIFEST_SECTION_TARGET_PATH => 'target:path';\n"
|
|
"push @EXPORT, qw(MANIFEST_SECTION_TARGET_PATH);\n"
|
|
"use constant MANIFEST_SECTION_TARGET_FILE => 'target:file';\n"
|
|
"push @EXPORT, qw(MANIFEST_SECTION_TARGET_FILE);\n"
|
|
"use constant MANIFEST_SECTION_TARGET_LINK => 'target:link';\n"
|
|
"push @EXPORT, qw(MANIFEST_SECTION_TARGET_LINK);\n"
|
|
"\n\n"
|
|
"use constant MANIFEST_KEY_ARCHIVE_START => 'backup-archive-start';\n"
|
|
"push @EXPORT, qw(MANIFEST_KEY_ARCHIVE_START);\n"
|
|
"use constant MANIFEST_KEY_ARCHIVE_STOP => 'backup-archive-stop';\n"
|
|
"push @EXPORT, qw(MANIFEST_KEY_ARCHIVE_STOP);\n"
|
|
"use constant MANIFEST_KEY_LABEL => 'backup-label';\n"
|
|
"push @EXPORT, qw(MANIFEST_KEY_LABEL);\n"
|
|
"use constant MANIFEST_KEY_LSN_START => 'backup-lsn-start';\n"
|
|
"push @EXPORT, qw(MANIFEST_KEY_LSN_START);\n"
|
|
"use constant MANIFEST_KEY_LSN_STOP => 'backup-lsn-stop';\n"
|
|
"push @EXPORT, qw(MANIFEST_KEY_LSN_STOP);\n"
|
|
"use constant MANIFEST_KEY_PRIOR => 'backup-prior';\n"
|
|
"push @EXPORT, qw(MANIFEST_KEY_PRIOR);\n"
|
|
"use constant MANIFEST_KEY_TIMESTAMP_COPY_START => 'backup-timestamp-copy-start';\n"
|
|
"push @EXPORT, qw(MANIFEST_KEY_TIMESTAMP_COPY_START);\n"
|
|
"use constant MANIFEST_KEY_TIMESTAMP_START => 'backup-timestamp-start';\n"
|
|
"push @EXPORT, qw(MANIFEST_KEY_TIMESTAMP_START);\n"
|
|
"use constant MANIFEST_KEY_TIMESTAMP_STOP => 'backup-timestamp-stop';\n"
|
|
"push @EXPORT, qw(MANIFEST_KEY_TIMESTAMP_STOP);\n"
|
|
"use constant MANIFEST_KEY_TYPE => 'backup-type';\n"
|
|
"push @EXPORT, qw(MANIFEST_KEY_TYPE);\n"
|
|
"\n\n"
|
|
"use constant MANIFEST_KEY_BACKUP_STANDBY => 'option-' . cfgOptionName(CFGOPT_BACKUP_STANDBY);\n"
|
|
"push @EXPORT, qw(MANIFEST_KEY_BACKUP_STANDBY);\n"
|
|
"use constant MANIFEST_KEY_HARDLINK => 'option-hardlink';\n"
|
|
"push @EXPORT, qw(MANIFEST_KEY_HARDLINK);\n"
|
|
"use constant MANIFEST_KEY_ARCHIVE_CHECK => 'option-' . cfgOptionName(CFGOPT_ARCHIVE_CHECK);\n"
|
|
"push @EXPORT, qw(MANIFEST_KEY_ARCHIVE_CHECK);\n"
|
|
"use constant MANIFEST_KEY_ARCHIVE_COPY => 'option-' .cfgOptionName(CFGOPT_ARCHIVE_COPY);\n"
|
|
"push @EXPORT, qw(MANIFEST_KEY_ARCHIVE_COPY);\n"
|
|
"use constant MANIFEST_KEY_BUFFER_SIZE => 'option-' . cfgOptionName(CFGOPT_BUFFER_SIZE);\n"
|
|
"push @EXPORT, qw(MANIFEST_KEY_BUFFER_SIZE);\n"
|
|
"use constant MANIFEST_KEY_CHECKSUM_PAGE => 'option-' . cfgOptionName(CFGOPT_CHECKSUM_PAGE);\n"
|
|
"push @EXPORT, qw(MANIFEST_KEY_CHECKSUM_PAGE);\n"
|
|
"use constant MANIFEST_KEY_COMPRESS => 'option-' . cfgOptionName(CFGOPT_COMPRESS);\n"
|
|
"push @EXPORT, qw(MANIFEST_KEY_COMPRESS);\n"
|
|
"use constant MANIFEST_KEY_COMPRESS_LEVEL => 'option-' . cfgOptionName(CFGOPT_COMPRESS_LEVEL);\n"
|
|
"push @EXPORT, qw(MANIFEST_KEY_COMPRESS_LEVEL);\n"
|
|
"use constant MANIFEST_KEY_COMPRESS_LEVEL_NETWORK => 'option-' . cfgOptionName(CFGOPT_COMPRESS_LEVEL_NETWORK);\n"
|
|
"push @EXPORT, qw(MANIFEST_KEY_COMPRESS_LEVEL_NETWORK);\n"
|
|
"use constant MANIFEST_KEY_ONLINE => 'option-' . cfgOptionName(CFGOPT_ONLINE);\n"
|
|
"push @EXPORT, qw(MANIFEST_KEY_ONLINE);\n"
|
|
"use constant MANIFEST_KEY_DELTA => 'option-' . cfgOptionName(CFGOPT_DELTA);\n"
|
|
"push @EXPORT, qw(MANIFEST_KEY_DELTA);\n"
|
|
"use constant MANIFEST_KEY_PROCESS_MAX => 'option-' . cfgOptionName(CFGOPT_PROCESS_MAX);\n"
|
|
"push @EXPORT, qw(MANIFEST_KEY_PROCESS_MAX);\n"
|
|
"\n\n"
|
|
"use constant MANIFEST_KEY_DB_ID => 'db-id';\n"
|
|
"push @EXPORT, qw(MANIFEST_KEY_DB_ID);\n"
|
|
"use constant MANIFEST_KEY_SYSTEM_ID => 'db-system-id';\n"
|
|
"push @EXPORT, qw(MANIFEST_KEY_SYSTEM_ID);\n"
|
|
"use constant MANIFEST_KEY_CATALOG => 'db-catalog-version';\n"
|
|
"push @EXPORT, qw(MANIFEST_KEY_CATALOG);\n"
|
|
"use constant MANIFEST_KEY_CONTROL => 'db-control-version';\n"
|
|
"push @EXPORT, qw(MANIFEST_KEY_CONTROL);\n"
|
|
"use constant MANIFEST_KEY_DB_LAST_SYSTEM_ID => 'db-last-system-id';\n"
|
|
"push @EXPORT, qw(MANIFEST_KEY_DB_LAST_SYSTEM_ID);\n"
|
|
"use constant MANIFEST_KEY_DB_VERSION => 'db-version';\n"
|
|
"push @EXPORT, qw(MANIFEST_KEY_DB_VERSION);\n"
|
|
"\n\n"
|
|
"use constant MANIFEST_SUBKEY_CHECKSUM => 'checksum';\n"
|
|
"push @EXPORT, qw(MANIFEST_SUBKEY_CHECKSUM);\n"
|
|
"use constant MANIFEST_SUBKEY_CHECKSUM_PAGE => 'checksum-page';\n"
|
|
"push @EXPORT, qw(MANIFEST_SUBKEY_CHECKSUM_PAGE);\n"
|
|
"use constant MANIFEST_SUBKEY_CHECKSUM_PAGE_ERROR => 'checksum-page-error';\n"
|
|
"push @EXPORT, qw(MANIFEST_SUBKEY_CHECKSUM_PAGE_ERROR);\n"
|
|
"use constant MANIFEST_SUBKEY_DESTINATION => 'destination';\n"
|
|
"push @EXPORT, qw(MANIFEST_SUBKEY_DESTINATION);\n"
|
|
"use constant MANIFEST_SUBKEY_FILE => 'file';\n"
|
|
"push @EXPORT, qw(MANIFEST_SUBKEY_FILE);\n"
|
|
"use constant MANIFEST_SUBKEY_FUTURE => 'future';\n"
|
|
"push @EXPORT, qw(MANIFEST_SUBKEY_FUTURE);\n"
|
|
"use constant MANIFEST_SUBKEY_GROUP => 'group';\n"
|
|
"push @EXPORT, qw(MANIFEST_SUBKEY_GROUP);\n"
|
|
"use constant MANIFEST_SUBKEY_MASTER => 'master';\n"
|
|
"push @EXPORT, qw(MANIFEST_SUBKEY_MASTER);\n"
|
|
"use constant MANIFEST_SUBKEY_MODE => 'mode';\n"
|
|
"push @EXPORT, qw(MANIFEST_SUBKEY_MODE);\n"
|
|
"use constant MANIFEST_SUBKEY_TIMESTAMP => 'timestamp';\n"
|
|
"push @EXPORT, qw(MANIFEST_SUBKEY_TIMESTAMP);\n"
|
|
"use constant MANIFEST_SUBKEY_TYPE => 'type';\n"
|
|
"push @EXPORT, qw(MANIFEST_SUBKEY_TYPE);\n"
|
|
"use constant MANIFEST_SUBKEY_PATH => 'path';\n"
|
|
"push @EXPORT, qw(MANIFEST_SUBKEY_PATH);\n"
|
|
"use constant MANIFEST_SUBKEY_REFERENCE => 'reference';\n"
|
|
"push @EXPORT, qw(MANIFEST_SUBKEY_REFERENCE);\n"
|
|
"use constant MANIFEST_SUBKEY_REPO_SIZE => 'repo-size';\n"
|
|
"push @EXPORT, qw(MANIFEST_SUBKEY_REPO_SIZE);\n"
|
|
"use constant MANIFEST_SUBKEY_SIZE => 'size';\n"
|
|
"push @EXPORT, qw(MANIFEST_SUBKEY_SIZE);\n"
|
|
"use constant MANIFEST_SUBKEY_TABLESPACE_ID => 'tablespace-id';\n"
|
|
"push @EXPORT, qw(MANIFEST_SUBKEY_TABLESPACE_ID);\n"
|
|
"use constant MANIFEST_SUBKEY_TABLESPACE_NAME => 'tablespace-name';\n"
|
|
"push @EXPORT, qw(MANIFEST_SUBKEY_TABLESPACE_NAME);\n"
|
|
"use constant MANIFEST_SUBKEY_USER => 'user';\n"
|
|
"push @EXPORT, qw(MANIFEST_SUBKEY_USER);\n"
|
|
"\n\n\n\n"
|
|
"use constant DB_PATH_ARCHIVESTATUS => 'archive_status';\n"
|
|
"push @EXPORT, qw(DB_PATH_ARCHIVESTATUS);\n"
|
|
"use constant DB_PATH_BASE => 'base';\n"
|
|
"push @EXPORT, qw(DB_PATH_BASE);\n"
|
|
"use constant DB_PATH_GLOBAL => 'global';\n"
|
|
"push @EXPORT, qw(DB_PATH_GLOBAL);\n"
|
|
"use constant DB_PATH_PGDYNSHMEM => 'pg_dynshmem';\n"
|
|
"push @EXPORT, qw(DB_PATH_PGDYNSHMEM);\n"
|
|
"use constant DB_PATH_PGMULTIXACT => 'pg_multixact';\n"
|
|
"push @EXPORT, qw(DB_PATH_PGMULTIXACT);\n"
|
|
"use constant DB_PATH_PGNOTIFY => 'pg_notify';\n"
|
|
"push @EXPORT, qw(DB_PATH_PGNOTIFY);\n"
|
|
"use constant DB_PATH_PGREPLSLOT => 'pg_replslot';\n"
|
|
"push @EXPORT, qw(DB_PATH_PGREPLSLOT);\n"
|
|
"use constant DB_PATH_PGSERIAL => 'pg_serial';\n"
|
|
"push @EXPORT, qw(DB_PATH_PGSERIAL);\n"
|
|
"use constant DB_PATH_PGSNAPSHOTS => 'pg_snapshots';\n"
|
|
"push @EXPORT, qw(DB_PATH_PGSNAPSHOTS);\n"
|
|
"use constant DB_PATH_PGSTATTMP => 'pg_stat_tmp';\n"
|
|
"push @EXPORT, qw(DB_PATH_PGSTATTMP);\n"
|
|
"use constant DB_PATH_PGSUBTRANS => 'pg_subtrans';\n"
|
|
"push @EXPORT, qw(DB_PATH_PGSUBTRANS);\n"
|
|
"use constant DB_PATH_PGTBLSPC => 'pg_tblspc';\n"
|
|
"push @EXPORT, qw(DB_PATH_PGTBLSPC);\n"
|
|
"\n"
|
|
"use constant DB_FILE_BACKUPLABEL => 'backup_label';\n"
|
|
"push @EXPORT, qw(DB_FILE_BACKUPLABEL);\n"
|
|
"use constant DB_FILE_BACKUPLABELOLD => DB_FILE_BACKUPLABEL . '.old';\n"
|
|
"push @EXPORT, qw(DB_FILE_BACKUPLABELOLD);\n"
|
|
"use constant DB_FILE_PGCONTROL => DB_PATH_GLOBAL . '/pg_control';\n"
|
|
"push @EXPORT, qw(DB_FILE_PGCONTROL);\n"
|
|
"use constant DB_FILE_PGFILENODEMAP => 'pg_filenode.map';\n"
|
|
"push @EXPORT, qw(DB_FILE_PGFILENODEMAP);\n"
|
|
"use constant DB_FILE_PGINTERNALINIT => 'pg_internal.init';\n"
|
|
"push @EXPORT, qw(DB_FILE_PGINTERNALINIT);\n"
|
|
"use constant DB_FILE_PGVERSION => 'PG_VERSION';\n"
|
|
"push @EXPORT, qw(DB_FILE_PGVERSION);\n"
|
|
"use constant DB_FILE_POSTGRESQLAUTOCONFTMP => 'postgresql.auto.conf.tmp';\n"
|
|
"push @EXPORT, qw(DB_FILE_POSTGRESQLAUTOCONFTMP);\n"
|
|
"use constant DB_FILE_POSTMASTEROPTS => 'postmaster.opts';\n"
|
|
"push @EXPORT, qw(DB_FILE_POSTMASTEROPTS);\n"
|
|
"use constant DB_FILE_POSTMASTERPID => 'postmaster.pid';\n"
|
|
"push @EXPORT, qw(DB_FILE_POSTMASTERPID);\n"
|
|
"use constant DB_FILE_RECOVERYCONF => 'recovery.conf';\n"
|
|
"push @EXPORT, qw(DB_FILE_RECOVERYCONF);\n"
|
|
"use constant DB_FILE_RECOVERYDONE => 'recovery.done';\n"
|
|
"push @EXPORT, qw(DB_FILE_RECOVERYDONE);\n"
|
|
"use constant DB_FILE_TABLESPACEMAP => 'tablespace_map';\n"
|
|
"push @EXPORT, qw(DB_FILE_TABLESPACEMAP);\n"
|
|
"\n"
|
|
"use constant DB_FILE_PREFIX_TMP => 'pgsql_tmp';\n"
|
|
"push @EXPORT, qw(DB_FILE_PREFIX_TMP);\n"
|
|
"\n\n\n\n"
|
|
"use constant MANIFEST_PATH_BASE => MANIFEST_TARGET_PGDATA . '/' . DB_PATH_BASE;\n"
|
|
"push @EXPORT, qw(MANIFEST_PATH_BASE);\n"
|
|
"use constant MANIFEST_PATH_GLOBAL => MANIFEST_TARGET_PGDATA . '/' . DB_PATH_GLOBAL;\n"
|
|
"push @EXPORT, qw(MANIFEST_PATH_GLOBAL);\n"
|
|
"use constant MANIFEST_PATH_PGDYNSHMEM => MANIFEST_TARGET_PGDATA . '/' . DB_PATH_PGDYNSHMEM;\n"
|
|
"push @EXPORT, qw(MANIFEST_PATH_PGDYNSHMEM);\n"
|
|
"use constant MANIFEST_PATH_PGMULTIXACT => MANIFEST_TARGET_PGDATA . '/' . DB_PATH_PGMULTIXACT;\n"
|
|
"push @EXPORT, qw(MANIFEST_PATH_PGMULTIXACT);\n"
|
|
"use constant MANIFEST_PATH_PGNOTIFY => MANIFEST_TARGET_PGDATA . '/' . DB_PATH_PGNOTIFY;\n"
|
|
"push @EXPORT, qw(MANIFEST_PATH_PGNOTIFY);\n"
|
|
"use constant MANIFEST_PATH_PGREPLSLOT => MANIFEST_TARGET_PGDATA . '/' . DB_PATH_PGREPLSLOT;\n"
|
|
"push @EXPORT, qw(MANIFEST_PATH_PGREPLSLOT);\n"
|
|
"use constant MANIFEST_PATH_PGSERIAL => MANIFEST_TARGET_PGDATA . '/' . DB_PATH_PGSERIAL;\n"
|
|
"push @EXPORT, qw(MANIFEST_PATH_PGSERIAL);\n"
|
|
"use constant MANIFEST_PATH_PGSNAPSHOTS => MANIFEST_TARGET_PGDATA . '/' . DB_PATH_PGSNAPSHOTS;\n"
|
|
"push @EXPORT, qw(MANIFEST_PATH_PGSNAPSHOTS);\n"
|
|
"use constant MANIFEST_PATH_PGSTATTMP => MANIFEST_TARGET_PGDATA . '/' . DB_PATH_PGSTATTMP;\n"
|
|
"push @EXPORT, qw(MANIFEST_PATH_PGSTATTMP);\n"
|
|
"use constant MANIFEST_PATH_PGSUBTRANS => MANIFEST_TARGET_PGDATA . '/' . DB_PATH_PGSUBTRANS;\n"
|
|
"push @EXPORT, qw(MANIFEST_PATH_PGSUBTRANS);\n"
|
|
"use constant MANIFEST_PATH_PGTBLSPC => MANIFEST_TARGET_PGDATA . '/' . DB_PATH_PGTBLSPC;\n"
|
|
"push @EXPORT, qw(MANIFEST_PATH_PGTBLSPC);\n"
|
|
"\n"
|
|
"use constant MANIFEST_FILE_BACKUPLABEL => MANIFEST_TARGET_PGDATA . '/' . DB_FILE_BACKUPLABEL;\n"
|
|
"push @EXPORT, qw(MANIFEST_FILE_BACKUPLABEL);\n"
|
|
"use constant MANIFEST_FILE_BACKUPLABELOLD => MANIFEST_TARGET_PGDATA . '/' . DB_FILE_BACKUPLABELOLD;\n"
|
|
"push @EXPORT, qw(MANIFEST_FILE_BACKUPLABELOLD);\n"
|
|
"use constant MANIFEST_FILE_PGCONTROL => MANIFEST_TARGET_PGDATA . '/' . DB_FILE_PGCONTROL;\n"
|
|
"push @EXPORT, qw(MANIFEST_FILE_PGCONTROL);\n"
|
|
"use constant MANIFEST_FILE_POSTGRESQLAUTOCONFTMP => MANIFEST_TARGET_PGDATA . '/' . DB_FILE_POSTGRESQLAUTOCONFTMP;\n"
|
|
"push @EXPORT, qw(MANIFEST_FILE_PGCONTROL);\n"
|
|
"use constant MANIFEST_FILE_POSTMASTEROPTS => MANIFEST_TARGET_PGDATA . '/' . DB_FILE_POSTMASTEROPTS;\n"
|
|
"push @EXPORT, qw(MANIFEST_FILE_POSTMASTEROPTS);\n"
|
|
"use constant MANIFEST_FILE_POSTMASTERPID => MANIFEST_TARGET_PGDATA . '/' . DB_FILE_POSTMASTERPID;\n"
|
|
"push @EXPORT, qw(MANIFEST_FILE_POSTMASTERPID);\n"
|
|
"use constant MANIFEST_FILE_RECOVERYCONF => MANIFEST_TARGET_PGDATA . '/' . DB_FILE_RECOVERYCONF;\n"
|
|
"push @EXPORT, qw(MANIFEST_FILE_RECOVERYCONF);\n"
|
|
"use constant MANIFEST_FILE_RECOVERYDONE => MANIFEST_TARGET_PGDATA . '/' . DB_FILE_RECOVERYDONE;\n"
|
|
"push @EXPORT, qw(MANIFEST_FILE_RECOVERYDONE);\n"
|
|
"use constant MANIFEST_FILE_TABLESPACEMAP => MANIFEST_TARGET_PGDATA . '/' . DB_FILE_TABLESPACEMAP;\n"
|
|
"push @EXPORT, qw(MANIFEST_FILE_TABLESPACEMAP);\n"
|
|
"\n\n\n\n"
|
|
"use constant DB_USER_OBJECT_MINIMUM_ID => 16384;\n"
|
|
"push @EXPORT, qw(DB_USER_OBJECT_MINIMUM_ID);\n"
|
|
"\n\n\n\n"
|
|
"sub new\n"
|
|
"{\n"
|
|
"my $class = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$strFileName,\n"
|
|
"$bLoad,\n"
|
|
"$oStorage,\n"
|
|
"$strDbVersion,\n"
|
|
"$iDbCatalogVersion,\n"
|
|
"$strCipherPass,\n"
|
|
"$strCipherPassSub,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->new', \\@_,\n"
|
|
"{name => 'strFileName', trace => true},\n"
|
|
"{name => 'bLoad', optional => true, default => true, trace => true},\n"
|
|
"{name => 'oStorage', optional => true, default => storageRepo(), trace => true},\n"
|
|
"{name => 'strDbVersion', optional => true, trace => true},\n"
|
|
"{name => 'iDbCatalogVersion', optional => true, trace => true},\n"
|
|
"{name => 'strCipherPass', optional => true, redact => true},\n"
|
|
"{name => 'strCipherPassSub', optional => true, redact => true},\n"
|
|
");\n"
|
|
"\n\n"
|
|
"my $self = $class->SUPER::new($strFileName, {bLoad => $bLoad, oStorage => $oStorage, strCipherPass => $strCipherPass,\n"
|
|
"strCipherPassSub => $strCipherPassSub});\n"
|
|
"\n\n"
|
|
"if (!$bLoad)\n"
|
|
"{\n"
|
|
"if (!(defined($strDbVersion) && defined($iDbCatalogVersion)))\n"
|
|
"{\n"
|
|
"confess &log(ASSERT, 'strDbVersion and iDbCatalogVersion must be provided with bLoad = false');\n"
|
|
"}\n"
|
|
"\n"
|
|
"$self->set(MANIFEST_SECTION_BACKUP_DB, MANIFEST_KEY_DB_VERSION, undef, $strDbVersion);\n"
|
|
"$self->numericSet(MANIFEST_SECTION_BACKUP_DB, MANIFEST_KEY_CATALOG, undef, $iDbCatalogVersion);\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"$self->{bBuilt} = $bLoad;\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'self', value => $self}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n\n\n"
|
|
"sub save\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my ($strOperation) = logDebugParam(__PACKAGE__ . '->save');\n"
|
|
"\n\n"
|
|
"$self->SUPER::save();\n"
|
|
"\n\n"
|
|
"return logDebugReturn($strOperation);\n"
|
|
"}\n"
|
|
"\n\n\n\n\n\n"
|
|
"sub get\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"my $strSection = shift;\n"
|
|
"my $strKey = shift;\n"
|
|
"my $strSubKey = shift;\n"
|
|
"my $bRequired = shift;\n"
|
|
"my $oDefault = shift;\n"
|
|
"\n"
|
|
"my $oValue = $self->SUPER::get($strSection, $strKey, $strSubKey, false);\n"
|
|
"\n"
|
|
"if (!defined($oValue) && defined($strKey) && defined($strSubKey) &&\n"
|
|
"($strSection eq MANIFEST_SECTION_TARGET_FILE || $strSection eq MANIFEST_SECTION_TARGET_PATH ||\n"
|
|
"$strSection eq MANIFEST_SECTION_TARGET_LINK) &&\n"
|
|
"($strSubKey eq MANIFEST_SUBKEY_USER || $strSubKey eq MANIFEST_SUBKEY_GROUP ||\n"
|
|
"$strSubKey eq MANIFEST_SUBKEY_MODE || $strSubKey eq MANIFEST_SUBKEY_MASTER) &&\n"
|
|
"$self->test($strSection, $strKey))\n"
|
|
"{\n"
|
|
"$oValue = $self->SUPER::get(\"${strSection}:default\", $strSubKey, undef, $bRequired, $oDefault);\n"
|
|
"}\n"
|
|
"else\n"
|
|
"{\n"
|
|
"$oValue = $self->SUPER::get($strSection, $strKey, $strSubKey, $bRequired, $oDefault);\n"
|
|
"}\n"
|
|
"\n"
|
|
"return $oValue;\n"
|
|
"}\n"
|
|
"\n\n\n\n\n\n"
|
|
"sub boolGet\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"my $strSection = shift;\n"
|
|
"my $strValue = shift;\n"
|
|
"my $strSubValue = shift;\n"
|
|
"my $bRequired = shift;\n"
|
|
"my $bDefault = shift;\n"
|
|
"\n"
|
|
"return $self->get($strSection, $strValue, $strSubValue, $bRequired,\n"
|
|
"defined($bDefault) ? ($bDefault ? INI_TRUE : INI_FALSE) : undef) ? true : false;\n"
|
|
"}\n"
|
|
"\n\n\n\n\n\n"
|
|
"sub numericGet\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"my $strSection = shift;\n"
|
|
"my $strValue = shift;\n"
|
|
"my $strSubValue = shift;\n"
|
|
"my $bRequired = shift;\n"
|
|
"my $nDefault = shift;\n"
|
|
"\n"
|
|
"return $self->get($strSection, $strValue, $strSubValue, $bRequired,\n"
|
|
"defined($nDefault) ? $nDefault + 0 : undef) + 0;\n"
|
|
"}\n"
|
|
"\n\n\n\n\n\n"
|
|
"sub tablespacePathGet\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n"
|
|
"return('PG_' . $self->get(MANIFEST_SECTION_BACKUP_DB, MANIFEST_KEY_DB_VERSION) .\n"
|
|
"'_' . $self->get(MANIFEST_SECTION_BACKUP_DB, MANIFEST_KEY_CATALOG));\n"
|
|
"}\n"
|
|
"\n\n\n\n\n\n"
|
|
"sub dbPathGet\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"my $strDbPath = shift;\n"
|
|
"my $strFile = shift;\n"
|
|
"\n"
|
|
"my $strDbFile = defined($strDbPath) ? \"${strDbPath}/\" : '';\n"
|
|
"\n"
|
|
"if (index($strFile, MANIFEST_TARGET_PGDATA . '/') == 0)\n"
|
|
"{\n"
|
|
"$strDbFile .= substr($strFile, length(MANIFEST_TARGET_PGDATA) + 1);\n"
|
|
"}\n"
|
|
"else\n"
|
|
"{\n"
|
|
"$strDbFile .= $strFile;\n"
|
|
"}\n"
|
|
"\n"
|
|
"return $strDbFile;\n"
|
|
"}\n"
|
|
"\n\n\n\n\n\n"
|
|
"sub repoPathGet\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"my $strTarget = shift;\n"
|
|
"my $strFile = shift;\n"
|
|
"\n"
|
|
"my $strRepoFile = $strTarget;\n"
|
|
"\n"
|
|
"if ($self->isTargetTablespace($strTarget) &&\n"
|
|
"($self->dbVersion() >= PG_VERSION_90))\n"
|
|
"{\n"
|
|
"$strRepoFile .= '/' . $self->tablespacePathGet();\n"
|
|
"}\n"
|
|
"\n"
|
|
"if (defined($strFile))\n"
|
|
"{\n"
|
|
"$strRepoFile .= \"/${strFile}\";\n"
|
|
"}\n"
|
|
"\n"
|
|
"return $strRepoFile;\n"
|
|
"}\n"
|
|
"\n\n\n\n\n\n"
|
|
"sub isTargetValid\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"my $strTarget = shift;\n"
|
|
"my $bError = shift;\n"
|
|
"\n"
|
|
"if (!defined($strTarget))\n"
|
|
"{\n"
|
|
"confess &log(ASSERT, 'target is not defined');\n"
|
|
"}\n"
|
|
"\n"
|
|
"if (!$self->test(MANIFEST_SECTION_BACKUP_TARGET, $strTarget))\n"
|
|
"{\n"
|
|
"if (defined($bError) && $bError)\n"
|
|
"{\n"
|
|
"confess &log(ASSERT, \"${strTarget} is not a valid target\");\n"
|
|
"}\n"
|
|
"\n"
|
|
"return false;\n"
|
|
"}\n"
|
|
"\n"
|
|
"return true;\n"
|
|
"}\n"
|
|
"\n\n\n\n\n\n"
|
|
"sub isTargetLink\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"my $strTarget = shift;\n"
|
|
"\n"
|
|
"$self->isTargetValid($strTarget, true);\n"
|
|
"\n"
|
|
"return $self->test(MANIFEST_SECTION_BACKUP_TARGET, $strTarget, MANIFEST_SUBKEY_TYPE, MANIFEST_VALUE_LINK);\n"
|
|
"}\n"
|
|
"\n\n\n\n\n\n"
|
|
"sub isTargetFile\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"my $strTarget = shift;\n"
|
|
"\n"
|
|
"$self->isTargetValid($strTarget, true);\n"
|
|
"\n"
|
|
"return $self->test(MANIFEST_SECTION_BACKUP_TARGET, $strTarget, MANIFEST_SUBKEY_FILE);\n"
|
|
"}\n"
|
|
"\n\n\n\n\n\n"
|
|
"sub isTargetTablespace\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"my $strTarget = shift;\n"
|
|
"\n"
|
|
"$self->isTargetValid($strTarget, true);\n"
|
|
"\n"
|
|
"return $self->test(MANIFEST_SECTION_BACKUP_TARGET, $strTarget, MANIFEST_SUBKEY_TABLESPACE_ID);\n"
|
|
"}\n"
|
|
"\n\n\n\n\n\n"
|
|
"sub checkDelta\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$strLastBackupSource,\n"
|
|
"$bOnlineSame,\n"
|
|
"$strTimelineCurrent,\n"
|
|
"$strTimelineLast,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->checkDelta', \\@_,\n"
|
|
"{name => 'strLastBackupSource'},\n"
|
|
"{name => 'bOnlineSame'},\n"
|
|
"{name => 'strTimelineCurrent', required => false},\n"
|
|
"{name => 'strTimelineLast', required => false},\n"
|
|
");\n"
|
|
"\n"
|
|
"my $bDelta = false;\n"
|
|
"\n\n"
|
|
"if (defined($strTimelineLast) && defined($strTimelineCurrent))\n"
|
|
"{\n"
|
|
"\n"
|
|
"if ($strTimelineLast ne $strTimelineCurrent)\n"
|
|
"{\n"
|
|
"&log(WARN, \"a timeline switch has occurred since the ${strLastBackupSource} backup, enabling delta checksum\");\n"
|
|
"$bDelta = true;\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if (!$bDelta && !$bOnlineSame)\n"
|
|
"{\n"
|
|
"&log(WARN, \"the online option has changed since the ${strLastBackupSource} backup, enabling delta checksum\");\n"
|
|
"$bDelta = true;\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'bDelta', value => $bDelta, trace => true},\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n\n\n"
|
|
"sub checkDeltaFile\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$stryFileList,\n"
|
|
"$oPriorManifest,\n"
|
|
"$lTimeBegin,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->checkDeltaFile', \\@_,\n"
|
|
"{name => 'stryFileList'},\n"
|
|
"{name => 'oPriorManifest', required => false},\n"
|
|
"{name => 'lTimeBegin', required => false},\n"
|
|
");\n"
|
|
"\n"
|
|
"my $bDelta = false;\n"
|
|
"\n\n"
|
|
"foreach my $strName (@{$stryFileList})\n"
|
|
"{\n"
|
|
"\n\n"
|
|
"if (defined($lTimeBegin) &&\n"
|
|
"($self->numericGet(MANIFEST_SECTION_TARGET_FILE, $strName, MANIFEST_SUBKEY_TIMESTAMP) > $lTimeBegin ||\n"
|
|
"(defined($oPriorManifest) &&\n"
|
|
"$oPriorManifest->test(MANIFEST_SECTION_TARGET_FILE, $strName, MANIFEST_SUBKEY_FUTURE, 'y'))))\n"
|
|
"{\n"
|
|
"&log(WARN, \"file $strName has timestamp in the future, enabling delta checksum\");\n"
|
|
"$bDelta = true;\n"
|
|
"last;\n"
|
|
"}\n"
|
|
"\n\n\n"
|
|
"if (defined($oPriorManifest) && $oPriorManifest->test(MANIFEST_SECTION_TARGET_FILE, $strName) &&\n"
|
|
"($self->numericGet(MANIFEST_SECTION_TARGET_FILE, $strName, MANIFEST_SUBKEY_TIMESTAMP) <\n"
|
|
"$oPriorManifest->numericGet(MANIFEST_SECTION_TARGET_FILE, $strName, MANIFEST_SUBKEY_TIMESTAMP) ||\n"
|
|
"($self->numericGet(MANIFEST_SECTION_TARGET_FILE, $strName, MANIFEST_SUBKEY_SIZE) !=\n"
|
|
"$oPriorManifest->numericGet(MANIFEST_SECTION_TARGET_FILE, $strName, MANIFEST_SUBKEY_SIZE) &&\n"
|
|
"$self->numericGet(MANIFEST_SECTION_TARGET_FILE, $strName, MANIFEST_SUBKEY_TIMESTAMP) ==\n"
|
|
"$oPriorManifest->numericGet(MANIFEST_SECTION_TARGET_FILE, $strName, MANIFEST_SUBKEY_TIMESTAMP))))\n"
|
|
"{\n"
|
|
"&log(WARN, \"file $strName timestamp in the past or size changed but timestamp did not, enabling delta checksum\");\n"
|
|
"$bDelta = true;\n"
|
|
"last;\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'bDelta', value => $bDelta, trace => true},\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n\n\n"
|
|
"sub build\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$oStorageDbMaster,\n"
|
|
"$strPath,\n"
|
|
"$oLastManifest,\n"
|
|
"$bOnline,\n"
|
|
"$bDelta,\n"
|
|
"$hTablespaceMap,\n"
|
|
"$hDatabaseMap,\n"
|
|
"$rhExclude,\n"
|
|
"$strTimelineCurrent,\n"
|
|
"$strTimelineLast,\n"
|
|
"$strLevel,\n"
|
|
"$bTablespace,\n"
|
|
"$strParentPath,\n"
|
|
"$strFilter,\n"
|
|
"$iLevel,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->build', \\@_,\n"
|
|
"{name => 'oStorageDbMaster'},\n"
|
|
"{name => 'strPath'},\n"
|
|
"{name => 'oLastManifest', required => false},\n"
|
|
"{name => 'bOnline'},\n"
|
|
"{name => 'bDelta'},\n"
|
|
"{name => 'hTablespaceMap', required => false},\n"
|
|
"{name => 'hDatabaseMap', required => false},\n"
|
|
"{name => 'rhExclude', required => false},\n"
|
|
"{name => 'strTimelineCurrent', required => false},\n"
|
|
"{name => 'strTimelineLast', required => false},\n"
|
|
"{name => 'strLevel', required => false},\n"
|
|
"{name => 'bTablespace', required => false},\n"
|
|
"{name => 'strParentPath', required => false},\n"
|
|
"{name => 'strFilter', required => false},\n"
|
|
"{name => 'iLevel', required => false, default => 0},\n"
|
|
");\n"
|
|
"\n\n"
|
|
"if ($iLevel >= 16)\n"
|
|
"{\n"
|
|
"confess &log(\n"
|
|
"ERROR,\n"
|
|
"\"recursion in manifest build exceeds depth of ${iLevel}: ${strLevel}\\n\" .\n"
|
|
"'HINT: is there a link loop in $PGDATA?',\n"
|
|
"ERROR_FORMAT);\n"
|
|
"}\n"
|
|
"\n"
|
|
"if (!defined($strLevel))\n"
|
|
"{\n"
|
|
"\n"
|
|
"if ($self->{bBuilt})\n"
|
|
"{\n"
|
|
"confess &log(ASSERT, \"manifest has already been built\");\n"
|
|
"}\n"
|
|
"\n"
|
|
"$self->{bBuilt} = true;\n"
|
|
"\n\n"
|
|
"$strLevel = MANIFEST_TARGET_PGDATA;\n"
|
|
"\n\n"
|
|
"if (!$bOnline && !defined($hTablespaceMap))\n"
|
|
"{\n"
|
|
"my $hTablespaceManifest = $oStorageDbMaster->manifest($strPath . '/' . DB_PATH_PGTBLSPC);\n"
|
|
"$hTablespaceMap = {};\n"
|
|
"\n"
|
|
"foreach my $strOid (sort(CORE::keys(%{$hTablespaceManifest})))\n"
|
|
"{\n"
|
|
"if ($strOid eq '.' or $strOid eq '..')\n"
|
|
"{\n"
|
|
"next;\n"
|
|
"}\n"
|
|
"\n"
|
|
"logDebugMisc($strOperation, \"found tablespace ${strOid} in offline mode\");\n"
|
|
"\n"
|
|
"$hTablespaceMap->{$strOid} = \"ts${strOid}\";\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if (defined($oLastManifest) && !$bDelta)\n"
|
|
"{\n"
|
|
"$bDelta = $self->checkDelta(\n"
|
|
"'last', $oLastManifest->boolTest(MANIFEST_SECTION_BACKUP_OPTION, MANIFEST_KEY_ONLINE, undef, $bOnline),\n"
|
|
"$strTimelineCurrent, $strTimelineLast);\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n"
|
|
"$self->set(MANIFEST_SECTION_BACKUP_TARGET, $strLevel, MANIFEST_SUBKEY_PATH, $strPath);\n"
|
|
"$self->set(MANIFEST_SECTION_BACKUP_TARGET, $strLevel, MANIFEST_SUBKEY_TYPE,\n"
|
|
"$strLevel eq MANIFEST_TARGET_PGDATA ? MANIFEST_VALUE_PATH : MANIFEST_VALUE_LINK);\n"
|
|
"\n"
|
|
"if ($bTablespace)\n"
|
|
"{\n"
|
|
"my $iTablespaceId = (split('\\/', $strLevel))[1];\n"
|
|
"\n"
|
|
"if (!defined($hTablespaceMap->{$iTablespaceId}))\n"
|
|
"{\n"
|
|
"confess &log(ASSERT, \"tablespace with oid ${iTablespaceId} not found in tablespace map\\n\" .\n"
|
|
"\"HINT: was a tablespace created or dropped during the backup?\");\n"
|
|
"}\n"
|
|
"\n"
|
|
"$self->set(MANIFEST_SECTION_BACKUP_TARGET, $strLevel, MANIFEST_SUBKEY_TABLESPACE_ID, $iTablespaceId);\n"
|
|
"$self->set(MANIFEST_SECTION_BACKUP_TARGET, $strLevel, MANIFEST_SUBKEY_TABLESPACE_NAME,\n"
|
|
"$hTablespaceMap->{$iTablespaceId});\n"
|
|
"}\n"
|
|
"\n"
|
|
"if (index($strPath, '/') != 0)\n"
|
|
"{\n"
|
|
"if (!defined($strParentPath))\n"
|
|
"{\n"
|
|
"confess &log(ASSERT, \"cannot get manifest for '${strPath}' when no parent path is specified\");\n"
|
|
"}\n"
|
|
"\n"
|
|
"$strPath = $oStorageDbMaster->pathAbsolute($strParentPath, $strPath);\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"my $hManifest = $oStorageDbMaster->manifest($strPath, {strFilter => $strFilter});\n"
|
|
"my $strManifestType = MANIFEST_VALUE_LINK;\n"
|
|
"\n\n"
|
|
"foreach my $strName (sort(CORE::keys(%{$hManifest})))\n"
|
|
"{\n"
|
|
"my $strFile = $strLevel;\n"
|
|
"\n"
|
|
"if ($strName ne '.')\n"
|
|
"{\n"
|
|
"if ($strManifestType eq MANIFEST_VALUE_LINK && $hManifest->{$strName}{type} eq 'l')\n"
|
|
"{\n"
|
|
"confess &log(ERROR, 'link \\'' .\n"
|
|
"$self->dbPathGet(\n"
|
|
"$self->get(MANIFEST_SECTION_BACKUP_TARGET, MANIFEST_TARGET_PGDATA, MANIFEST_SUBKEY_PATH), $strLevel) .\n"
|
|
"'\\' -> \\'' . $self->get(MANIFEST_SECTION_BACKUP_TARGET, $strLevel, MANIFEST_SUBKEY_PATH) .\n"
|
|
"'\\' cannot reference another link', ERROR_LINK_DESTINATION);\n"
|
|
"}\n"
|
|
"\n"
|
|
"if ($strManifestType eq MANIFEST_VALUE_LINK)\n"
|
|
"{\n"
|
|
"$strFile = dirname($strFile);\n"
|
|
"$self->set(MANIFEST_SECTION_BACKUP_TARGET, $strLevel, MANIFEST_SUBKEY_PATH,\n"
|
|
"dirname($self->get(MANIFEST_SECTION_BACKUP_TARGET, $strLevel, MANIFEST_SUBKEY_PATH)));\n"
|
|
"$self->set(MANIFEST_SECTION_BACKUP_TARGET, $strLevel, MANIFEST_SUBKEY_FILE, $strName);\n"
|
|
"}\n"
|
|
"\n"
|
|
"$strFile .= \"/${strName}\";\n"
|
|
"}\n"
|
|
"else\n"
|
|
"{\n"
|
|
"$strManifestType = MANIFEST_VALUE_PATH;\n"
|
|
"}\n"
|
|
"\n\n\n"
|
|
"next if ($bOnline && $strFile =~ (qw{^} . MANIFEST_TARGET_PGDATA . qw{/} . $self->walPath() . '\\/') &&\n"
|
|
"$strFile !~ ('^' . MANIFEST_TARGET_PGDATA . qw{/} . $self->walPath() . qw{/} . DB_PATH_ARCHIVESTATUS . '$'));\n"
|
|
"\n\n\n"
|
|
"next if $strName =~ ('(^|\\/)' . DB_FILE_PREFIX_TMP);\n"
|
|
"\n\n"
|
|
"next if $strFile =~ ('^' . MANIFEST_PATH_PGDYNSHMEM . '\\/') && $self->dbVersion() >= PG_VERSION_94;\n"
|
|
"\n\n"
|
|
"next if $strFile =~ ('^' . MANIFEST_PATH_PGNOTIFY . '\\/') && $self->dbVersion() >= PG_VERSION_90;\n"
|
|
"\n\n"
|
|
"next if $strFile =~ ('^' . MANIFEST_PATH_PGREPLSLOT . '\\/') && $self->dbVersion() >= PG_VERSION_94;\n"
|
|
"\n\n"
|
|
"next if $strFile =~ ('^' . MANIFEST_PATH_PGSERIAL . '\\/') && $self->dbVersion() >= PG_VERSION_91;\n"
|
|
"\n\n"
|
|
"next if $strFile =~ ('^' . MANIFEST_PATH_PGSNAPSHOTS . '\\/') && $self->dbVersion() >= PG_VERSION_92;\n"
|
|
"\n\n\n"
|
|
"next if $strFile =~ ('^' . MANIFEST_PATH_PGSTATTMP . '\\/') && $self->dbVersion() >= PG_VERSION_84;\n"
|
|
"\n\n"
|
|
"next if $strFile =~ ('^' . MANIFEST_PATH_PGSUBTRANS . '\\/');\n"
|
|
"\n\n"
|
|
"next if $strFile =~ (DB_FILE_PGINTERNALINIT . '$');\n"
|
|
"\n\n"
|
|
"if ($strFile eq MANIFEST_FILE_POSTGRESQLAUTOCONFTMP ||\n"
|
|
"$strFile eq MANIFEST_FILE_BACKUPLABELOLD ||\n"
|
|
"$strFile eq MANIFEST_FILE_POSTMASTEROPTS ||\n"
|
|
"$strFile eq MANIFEST_FILE_POSTMASTERPID ||\n"
|
|
"$strFile eq MANIFEST_FILE_RECOVERYCONF || # recovery.conf - doesn't make sense to backup this file\n"
|
|
"$strFile eq MANIFEST_FILE_RECOVERYDONE) # recovery.done - doesn't make sense to backup this file\n"
|
|
"{\n"
|
|
"next;\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if ($self->dbVersion() >= PG_VERSION_90 && $hManifest->{$strName}{type} eq 'f')\n"
|
|
"{\n"
|
|
"\n"
|
|
"my $strDir = dirname($strName);\n"
|
|
"\n\n"
|
|
"if ($strDir =~ '^base\\/[0-9]+$' ||\n"
|
|
"$strDir =~ ('^' . $self->tablespacePathGet() . '\\/[0-9]+$'))\n"
|
|
"{\n"
|
|
"\n"
|
|
"my $strBaseName = basename($strName);\n"
|
|
"\n\n\n"
|
|
"if ($strBaseName =~ '^t[0-9]+\\_[0-9]+(|\\_(fsm|vm)){0,1}(\\.[0-9]+){0,1}$')\n"
|
|
"{\n"
|
|
"next;\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if ($self->dbVersion() >= PG_VERSION_91)\n"
|
|
"{\n"
|
|
"\n"
|
|
"if ($strBaseName =~ '^[0-9]+(|\\_(fsm|vm)){0,1}(\\.[0-9]+){0,1}$')\n"
|
|
"{\n"
|
|
"\n"
|
|
"my ($strFileNode) = $strBaseName =~ '^(\\d+)';\n"
|
|
"\n\n"
|
|
"$strFileNode = $strDir. \"/\" . $strFileNode . \"_init\";\n"
|
|
"\n\n"
|
|
"if (exists($hManifest->{$strFileNode}) && $hManifest->{$strFileNode}{type} eq 'f')\n"
|
|
"{\n"
|
|
"next;\n"
|
|
"}\n"
|
|
"}\n"
|
|
"}\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n"
|
|
"my $cType = $hManifest->{$strName}{type};\n"
|
|
"my $strSection = MANIFEST_SECTION_TARGET_PATH;\n"
|
|
"\n"
|
|
"if ($cType eq 'f')\n"
|
|
"{\n"
|
|
"$strSection = MANIFEST_SECTION_TARGET_FILE;\n"
|
|
"}\n"
|
|
"elsif ($cType eq 'l')\n"
|
|
"{\n"
|
|
"$strSection = MANIFEST_SECTION_TARGET_LINK;\n"
|
|
"}\n"
|
|
"elsif ($cType ne 'd')\n"
|
|
"{\n"
|
|
"confess &log(ASSERT, \"unrecognized file type $cType for file $strName\");\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"my $bTablespace = false;\n"
|
|
"\n"
|
|
"if (index($strName, DB_PATH_PGTBLSPC . '/') == 0 && $strLevel eq MANIFEST_TARGET_PGDATA)\n"
|
|
"{\n"
|
|
"$bTablespace = true;\n"
|
|
"$strFile = MANIFEST_TARGET_PGDATA . '/' . $strName;\n"
|
|
"\n\n"
|
|
"if ($hManifest->{$strName}{type} ne 'l')\n"
|
|
"{\n"
|
|
"confess &log(ERROR, \"${strName} is not a symlink - \" . DB_PATH_PGTBLSPC . ' should contain only symlinks',\n"
|
|
"ERROR_LINK_EXPECTED);\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if (index($hManifest->{$strName}{link_destination}, \"${strPath}/\") == 0 ||\n"
|
|
"(index($hManifest->{$strName}{link_destination}, '/') != 0 &&\n"
|
|
"index($oStorageDbMaster->pathAbsolute($strPath . '/' . DB_PATH_PGTBLSPC,\n"
|
|
"$hManifest->{$strName}{link_destination}) . '/', \"${strPath}/\") == 0))\n"
|
|
"{\n"
|
|
"confess &log(ERROR, 'tablespace symlink ' . $hManifest->{$strName}{link_destination} .\n"
|
|
"' destination must not be in $PGDATA', ERROR_TABLESPACE_IN_PGDATA);\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if (defined($rhExclude))\n"
|
|
"{\n"
|
|
"\n"
|
|
"my $strPgFile = $self->dbPathGet(undef, $strFile);\n"
|
|
"my $bExclude = false;\n"
|
|
"\n\n"
|
|
"foreach my $strExclude (sort(keys(%{$rhExclude})))\n"
|
|
"{\n"
|
|
"\n"
|
|
"if ($strExclude =~ /\\/$/)\n"
|
|
"{\n"
|
|
"if (index($strPgFile, $strExclude) == 0)\n"
|
|
"{\n"
|
|
"$bExclude = true;\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n"
|
|
"elsif ($strPgFile eq $strExclude || index($strPgFile, \"${strExclude}/\") == 0)\n"
|
|
"{\n"
|
|
"$bExclude = true;\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if ($bExclude)\n"
|
|
"{\n"
|
|
"&log(INFO, \"exclude ${strPgFile} from backup using '${strExclude}' exclusion\");\n"
|
|
"last;\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"next if $bExclude;\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if (defined($hManifest->{$strName}{user}))\n"
|
|
"{\n"
|
|
"$self->set($strSection, $strFile, MANIFEST_SUBKEY_USER, $hManifest->{$strName}{user});\n"
|
|
"}\n"
|
|
"else\n"
|
|
"{\n"
|
|
"$self->boolSet($strSection, $strFile, MANIFEST_SUBKEY_USER, false);\n"
|
|
"}\n"
|
|
"\n"
|
|
"if (defined($hManifest->{$strName}{group}))\n"
|
|
"{\n"
|
|
"$self->set($strSection, $strFile, MANIFEST_SUBKEY_GROUP, $hManifest->{$strName}{group});\n"
|
|
"}\n"
|
|
"else\n"
|
|
"{\n"
|
|
"$self->boolSet($strSection, $strFile, MANIFEST_SUBKEY_GROUP, false);\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if ($cType eq 'f' || $cType eq 'd')\n"
|
|
"{\n"
|
|
"$self->set($strSection, $strFile, MANIFEST_SUBKEY_MODE, $hManifest->{$strName}{mode});\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if ($cType eq 'f')\n"
|
|
"{\n"
|
|
"$self->set($strSection, $strFile, MANIFEST_SUBKEY_TIMESTAMP,\n"
|
|
"$hManifest->{$strName}{modification_time} + 0);\n"
|
|
"$self->set($strSection, $strFile, MANIFEST_SUBKEY_SIZE, $hManifest->{$strName}{size} + 0);\n"
|
|
"$self->boolSet($strSection, $strFile, MANIFEST_SUBKEY_MASTER,\n"
|
|
"($strFile eq MANIFEST_FILE_PGCONTROL || $self->isMasterFile($strFile)));\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if ($cType eq 'l')\n"
|
|
"{\n"
|
|
"my $strLinkDestination = $hManifest->{$strName}{link_destination};\n"
|
|
"$self->set($strSection, $strFile, MANIFEST_SUBKEY_DESTINATION, $strLinkDestination);\n"
|
|
"\n\n"
|
|
"my $strFilter;\n"
|
|
"\n"
|
|
"if ($bTablespace)\n"
|
|
"{\n"
|
|
"\n\n"
|
|
"if ($self->dbVersion() >= PG_VERSION_90)\n"
|
|
"{\n"
|
|
"$strFilter = $self->tablespacePathGet();\n"
|
|
"}\n"
|
|
"\n"
|
|
"$self->set(MANIFEST_SECTION_TARGET_PATH, MANIFEST_TARGET_PGTBLSPC, undef,\n"
|
|
"$self->get(MANIFEST_SECTION_TARGET_PATH, MANIFEST_TARGET_PGDATA));\n"
|
|
"\n\n"
|
|
"$strFile = substr($strFile, length(MANIFEST_TARGET_PGDATA) + 1);\n"
|
|
"}\n"
|
|
"\n"
|
|
"$bDelta = $self->build(\n"
|
|
"$oStorageDbMaster, $strLinkDestination, undef, $bOnline, $bDelta, $hTablespaceMap, $hDatabaseMap, $rhExclude, undef,\n"
|
|
"undef, $strFile, $bTablespace, dirname(\"${strPath}/${strName}\"), $strFilter, $iLevel + 1);\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if ($strLevel eq MANIFEST_TARGET_PGDATA)\n"
|
|
"{\n"
|
|
"my $bTimeInFuture = false;\n"
|
|
"\n\n\n\n\n\n"
|
|
"my $lTimeBegin =\n"
|
|
"$oStorageDbMaster->can('protocol') ?\n"
|
|
"$oStorageDbMaster->protocol()->cmdExecute(OP_WAIT, [$bOnline]) : waitRemainder($bOnline);\n"
|
|
"\n\n"
|
|
"$self->linkCheck();\n"
|
|
"\n"
|
|
"if (defined($oLastManifest))\n"
|
|
"{\n"
|
|
"$self->set(MANIFEST_SECTION_BACKUP, MANIFEST_KEY_PRIOR, undef,\n"
|
|
"$oLastManifest->get(MANIFEST_SECTION_BACKUP, MANIFEST_KEY_LABEL));\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"foreach my $strDbName (sort(keys(%{$hDatabaseMap})))\n"
|
|
"{\n"
|
|
"$self->numericSet(MANIFEST_SECTION_DB, $strDbName, MANIFEST_KEY_DB_ID,\n"
|
|
"$hDatabaseMap->{$strDbName}{&MANIFEST_KEY_DB_ID});\n"
|
|
"$self->numericSet(MANIFEST_SECTION_DB, $strDbName, MANIFEST_KEY_DB_LAST_SYSTEM_ID,\n"
|
|
"$hDatabaseMap->{$strDbName}{&MANIFEST_KEY_DB_LAST_SYSTEM_ID});\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if (!$bDelta)\n"
|
|
"{\n"
|
|
"my @stryFileList = $self->keys(MANIFEST_SECTION_TARGET_FILE);\n"
|
|
"\n"
|
|
"if (@stryFileList)\n"
|
|
"{\n"
|
|
"$bDelta = $self->checkDeltaFile(\\@stryFileList, $oLastManifest, $lTimeBegin);\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"foreach my $strName ($self->keys(MANIFEST_SECTION_TARGET_FILE))\n"
|
|
"{\n"
|
|
"\n\n"
|
|
"if ($self->numericGet(MANIFEST_SECTION_TARGET_FILE, $strName, MANIFEST_SUBKEY_TIMESTAMP) > $lTimeBegin ||\n"
|
|
"(defined($oLastManifest) &&\n"
|
|
"$oLastManifest->test(MANIFEST_SECTION_TARGET_FILE, $strName, MANIFEST_SUBKEY_FUTURE, 'y')))\n"
|
|
"{\n"
|
|
"$bTimeInFuture = true;\n"
|
|
"\n\n"
|
|
"if ($self->numericGet(MANIFEST_SECTION_TARGET_FILE, $strName, MANIFEST_SUBKEY_TIMESTAMP) > $lTimeBegin)\n"
|
|
"{\n"
|
|
"$self->set(MANIFEST_SECTION_TARGET_FILE, $strName, MANIFEST_SUBKEY_FUTURE, 'y');\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n\n\n\n"
|
|
"elsif (defined($oLastManifest) && $oLastManifest->test(MANIFEST_SECTION_TARGET_FILE, $strName) &&\n"
|
|
"$self->numericGet(MANIFEST_SECTION_TARGET_FILE, $strName, MANIFEST_SUBKEY_SIZE) ==\n"
|
|
"$oLastManifest->numericGet(MANIFEST_SECTION_TARGET_FILE, $strName, MANIFEST_SUBKEY_SIZE) &&\n"
|
|
"($bDelta || ($self->numericGet(MANIFEST_SECTION_TARGET_FILE, $strName, MANIFEST_SUBKEY_SIZE) == 0 ||\n"
|
|
"$self->numericGet(MANIFEST_SECTION_TARGET_FILE, $strName, MANIFEST_SUBKEY_TIMESTAMP) ==\n"
|
|
"$oLastManifest->numericGet(MANIFEST_SECTION_TARGET_FILE, $strName, MANIFEST_SUBKEY_TIMESTAMP))))\n"
|
|
"{\n"
|
|
"\n"
|
|
"if ($oLastManifest->test(MANIFEST_SECTION_TARGET_FILE, $strName, MANIFEST_SUBKEY_REFERENCE))\n"
|
|
"{\n"
|
|
"$self->set(MANIFEST_SECTION_TARGET_FILE, $strName, MANIFEST_SUBKEY_REFERENCE,\n"
|
|
"$oLastManifest->get(MANIFEST_SECTION_TARGET_FILE, $strName, MANIFEST_SUBKEY_REFERENCE));\n"
|
|
"}\n"
|
|
"\n"
|
|
"else\n"
|
|
"{\n"
|
|
"$self->set(MANIFEST_SECTION_TARGET_FILE, $strName, MANIFEST_SUBKEY_REFERENCE,\n"
|
|
"$oLastManifest->get(MANIFEST_SECTION_BACKUP, MANIFEST_KEY_LABEL));\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if ($oLastManifest->test(MANIFEST_SECTION_TARGET_FILE, $strName, MANIFEST_SUBKEY_CHECKSUM))\n"
|
|
"{\n"
|
|
"$self->set(MANIFEST_SECTION_TARGET_FILE, $strName, MANIFEST_SUBKEY_CHECKSUM,\n"
|
|
"$oLastManifest->get(MANIFEST_SECTION_TARGET_FILE, $strName, MANIFEST_SUBKEY_CHECKSUM));\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if ($oLastManifest->test(MANIFEST_SECTION_TARGET_FILE, $strName, MANIFEST_SUBKEY_REPO_SIZE))\n"
|
|
"{\n"
|
|
"$self->set(MANIFEST_SECTION_TARGET_FILE, $strName, MANIFEST_SUBKEY_REPO_SIZE,\n"
|
|
"$oLastManifest->get(MANIFEST_SECTION_TARGET_FILE, $strName, MANIFEST_SUBKEY_REPO_SIZE));\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if ($oLastManifest->test(MANIFEST_SECTION_TARGET_FILE, $strName, MANIFEST_SUBKEY_MASTER))\n"
|
|
"{\n"
|
|
"$self->set(\n"
|
|
"MANIFEST_SECTION_TARGET_FILE, $strName, MANIFEST_SUBKEY_MASTER,\n"
|
|
"$oLastManifest->get(MANIFEST_SECTION_TARGET_FILE, $strName, MANIFEST_SUBKEY_MASTER));\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"my $bChecksumPage = $oLastManifest->get(\n"
|
|
"MANIFEST_SECTION_TARGET_FILE, $strName, MANIFEST_SUBKEY_CHECKSUM_PAGE, false);\n"
|
|
"\n"
|
|
"if (defined($bChecksumPage))\n"
|
|
"{\n"
|
|
"$self->boolSet(MANIFEST_SECTION_TARGET_FILE, $strName, MANIFEST_SUBKEY_CHECKSUM_PAGE, $bChecksumPage);\n"
|
|
"\n"
|
|
"if (!$bChecksumPage &&\n"
|
|
"$oLastManifest->test(MANIFEST_SECTION_TARGET_FILE, $strName, MANIFEST_SUBKEY_CHECKSUM_PAGE_ERROR))\n"
|
|
"{\n"
|
|
"$self->set(\n"
|
|
"MANIFEST_SECTION_TARGET_FILE, $strName, MANIFEST_SUBKEY_CHECKSUM_PAGE_ERROR,\n"
|
|
"$oLastManifest->get(MANIFEST_SECTION_TARGET_FILE, $strName, MANIFEST_SUBKEY_CHECKSUM_PAGE_ERROR));\n"
|
|
"}\n"
|
|
"}\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if ($bTimeInFuture)\n"
|
|
"{\n"
|
|
"&log(WARN, \"some files have timestamps in the future - they will be copied to prevent possible race conditions\");\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"$self->set(MANIFEST_SECTION_BACKUP, MANIFEST_KEY_TIMESTAMP_COPY_START, undef, $lTimeBegin + ($bOnline ? 1 : 0));\n"
|
|
"\n\n"
|
|
"$self->buildDefault();\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'bDelta', value => $bDelta, trace => true},\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n\n\n\n"
|
|
"sub linkCheck\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my ($strOperation) = logDebugParam(__PACKAGE__ . '->linkCheck');\n"
|
|
"\n\n"
|
|
"my $strBasePath = $self->get(MANIFEST_SECTION_BACKUP_TARGET, MANIFEST_TARGET_PGDATA, MANIFEST_SUBKEY_PATH);\n"
|
|
"\n"
|
|
"foreach my $strTargetParent ($self->keys(MANIFEST_SECTION_BACKUP_TARGET))\n"
|
|
"{\n"
|
|
"if ($self->isTargetLink($strTargetParent))\n"
|
|
"{\n"
|
|
"my $strParentPath = $self->get(MANIFEST_SECTION_BACKUP_TARGET, $strTargetParent, MANIFEST_SUBKEY_PATH);\n"
|
|
"my $strParentFile = $self->get(MANIFEST_SECTION_BACKUP_TARGET, $strTargetParent, MANIFEST_SUBKEY_FILE, false);\n"
|
|
"\n"
|
|
"foreach my $strTargetChild ($self->keys(MANIFEST_SECTION_BACKUP_TARGET))\n"
|
|
"{\n"
|
|
"if ($self->isTargetLink($strTargetChild) && $strTargetParent ne $strTargetChild)\n"
|
|
"{\n"
|
|
"my $strChildPath = $self->get(MANIFEST_SECTION_BACKUP_TARGET, $strTargetChild, MANIFEST_SUBKEY_PATH);\n"
|
|
"my $strChildFile = $self->get(MANIFEST_SECTION_BACKUP_TARGET, $strTargetParent, MANIFEST_SUBKEY_FILE, false);\n"
|
|
"\n"
|
|
"if (!(defined($strParentFile) && defined($strChildFile)) &&\n"
|
|
"index(\n"
|
|
"storageLocal()->pathAbsolute($strBasePath, $strChildPath) . '/',\n"
|
|
"storageLocal()->pathAbsolute($strBasePath, $strParentPath) . '/') == 0)\n"
|
|
"{\n"
|
|
"confess &log(ERROR, 'link ' . $self->dbPathGet($strBasePath, $strTargetChild) .\n"
|
|
"\" (${strChildPath}) references a subdirectory of or\" .\n"
|
|
"\" the same directory as link \" . $self->dbPathGet($strBasePath, $strTargetParent) .\n"
|
|
"\" (${strParentPath})\", ERROR_LINK_DESTINATION);\n"
|
|
"}\n"
|
|
"}\n"
|
|
"}\n"
|
|
"}\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n\n\n\n\n\n"
|
|
"sub fileAdd\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$strManifestFile,\n"
|
|
"$lModificationTime,\n"
|
|
"$lSize,\n"
|
|
"$strChecksum,\n"
|
|
"$bMaster,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->fileAdd', \\@_,\n"
|
|
"{name => 'strManifestFile'},\n"
|
|
"{name => 'lModificationTime'},\n"
|
|
"{name => 'lSize'},\n"
|
|
"{name => 'lChecksum'},\n"
|
|
"{name => 'bMaster'},\n"
|
|
");\n"
|
|
"\n\n"
|
|
"if (!$self->test(MANIFEST_SECTION_TARGET_FILE . ':default', MANIFEST_SUBKEY_USER) ||\n"
|
|
"!$self->test(MANIFEST_SECTION_TARGET_FILE . ':default', MANIFEST_SUBKEY_USER, undef,\n"
|
|
"$self->get(MANIFEST_SECTION_TARGET_PATH, MANIFEST_TARGET_PGDATA, MANIFEST_SUBKEY_USER)))\n"
|
|
"{\n"
|
|
"$self->set(MANIFEST_SECTION_TARGET_FILE, $strManifestFile, MANIFEST_SUBKEY_USER,\n"
|
|
"$self->get(MANIFEST_SECTION_TARGET_PATH, MANIFEST_TARGET_PGDATA, MANIFEST_SUBKEY_USER));\n"
|
|
"}\n"
|
|
"\n"
|
|
"if (!$self->test(MANIFEST_SECTION_TARGET_FILE . ':default', MANIFEST_SUBKEY_GROUP) ||\n"
|
|
"!$self->test(MANIFEST_SECTION_TARGET_FILE . ':default', MANIFEST_SUBKEY_GROUP, undef,\n"
|
|
"$self->get(MANIFEST_SECTION_TARGET_PATH, MANIFEST_TARGET_PGDATA, MANIFEST_SUBKEY_GROUP)))\n"
|
|
"{\n"
|
|
"$self->set(MANIFEST_SECTION_TARGET_FILE, $strManifestFile, MANIFEST_SUBKEY_GROUP,\n"
|
|
"$self->get(MANIFEST_SECTION_TARGET_PATH, MANIFEST_TARGET_PGDATA, MANIFEST_SUBKEY_GROUP));\n"
|
|
"}\n"
|
|
"\n"
|
|
"if (!$self->test(MANIFEST_SECTION_TARGET_FILE . ':default', MANIFEST_SUBKEY_MODE) ||\n"
|
|
"!$self->test(MANIFEST_SECTION_TARGET_FILE . ':default', MANIFEST_SUBKEY_MODE, undef, '0600'))\n"
|
|
"{\n"
|
|
"$self->set(MANIFEST_SECTION_TARGET_FILE, $strManifestFile, MANIFEST_SUBKEY_MODE, '0600');\n"
|
|
"}\n"
|
|
"\n"
|
|
"$self->set(MANIFEST_SECTION_TARGET_FILE, $strManifestFile, MANIFEST_SUBKEY_TIMESTAMP, $lModificationTime);\n"
|
|
"$self->set(MANIFEST_SECTION_TARGET_FILE, $strManifestFile, MANIFEST_SUBKEY_SIZE, $lSize);\n"
|
|
"$self->set(MANIFEST_SECTION_TARGET_FILE, $strManifestFile, MANIFEST_SUBKEY_CHECKSUM, $strChecksum);\n"
|
|
"$self->boolSet(MANIFEST_SECTION_TARGET_FILE, $strManifestFile, MANIFEST_SUBKEY_MASTER, $bMaster);\n"
|
|
"}\n"
|
|
"\n\n\n\n\n\n"
|
|
"sub buildDefault\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my ($strOperation) = logDebugParam(__PACKAGE__ . '->buildDefault');\n"
|
|
"\n\n"
|
|
"foreach my $strSection (&MANIFEST_SECTION_TARGET_FILE, &MANIFEST_SECTION_TARGET_PATH, &MANIFEST_SECTION_TARGET_LINK)\n"
|
|
"{\n"
|
|
"foreach my $strSubKey (&MANIFEST_SUBKEY_USER, &MANIFEST_SUBKEY_GROUP, &MANIFEST_SUBKEY_MODE, &MANIFEST_SUBKEY_MASTER)\n"
|
|
"{\n"
|
|
"\n"
|
|
"next if ($strSection eq MANIFEST_SECTION_TARGET_LINK && $strSubKey eq &MANIFEST_SUBKEY_MODE);\n"
|
|
"\n\n"
|
|
"next if ($strSection ne MANIFEST_SECTION_TARGET_FILE && $strSubKey eq &MANIFEST_SUBKEY_MASTER);\n"
|
|
"\n"
|
|
"my %oDefault;\n"
|
|
"my $iSectionTotal = 0;\n"
|
|
"\n"
|
|
"foreach my $strFile ($self->keys($strSection))\n"
|
|
"{\n"
|
|
"\n"
|
|
"next if (($strSubKey eq MANIFEST_SUBKEY_USER || $strSubKey eq MANIFEST_SUBKEY_GROUP) &&\n"
|
|
"$self->boolTest($strSection, $strFile, $strSubKey, false));\n"
|
|
"\n"
|
|
"my $strValue = $self->get($strSection, $strFile, $strSubKey);\n"
|
|
"\n"
|
|
"if (defined($oDefault{$strValue}))\n"
|
|
"{\n"
|
|
"$oDefault{$strValue}++;\n"
|
|
"}\n"
|
|
"else\n"
|
|
"{\n"
|
|
"$oDefault{$strValue} = 1;\n"
|
|
"}\n"
|
|
"\n"
|
|
"$iSectionTotal++;\n"
|
|
"}\n"
|
|
"\n"
|
|
"my $strMaxValue;\n"
|
|
"my $iMaxValueTotal = 0;\n"
|
|
"\n"
|
|
"foreach my $strValue (sort(keys(%oDefault)))\n"
|
|
"{\n"
|
|
"if ($oDefault{$strValue} > $iMaxValueTotal)\n"
|
|
"{\n"
|
|
"$iMaxValueTotal = $oDefault{$strValue};\n"
|
|
"$strMaxValue = $strValue;\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n"
|
|
"if (defined($strMaxValue) > 0 && $iMaxValueTotal > $iSectionTotal * MANIFEST_DEFAULT_MATCH_FACTOR)\n"
|
|
"{\n"
|
|
"if ($strSubKey eq MANIFEST_SUBKEY_MASTER)\n"
|
|
"{\n"
|
|
"$self->boolSet(\"${strSection}:default\", $strSubKey, undef, $strMaxValue);\n"
|
|
"}\n"
|
|
"else\n"
|
|
"{\n"
|
|
"$self->set(\"${strSection}:default\", $strSubKey, undef, $strMaxValue);\n"
|
|
"}\n"
|
|
"\n"
|
|
"foreach my $strFile ($self->keys($strSection))\n"
|
|
"{\n"
|
|
"if ($self->test($strSection, $strFile, $strSubKey, $strMaxValue))\n"
|
|
"{\n"
|
|
"$self->remove($strSection, $strFile, $strSubKey);\n"
|
|
"}\n"
|
|
"}\n"
|
|
"}\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"return logDebugReturn($strOperation);\n"
|
|
"}\n"
|
|
"\n\n\n\n\n\n"
|
|
"sub validate\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my ($strOperation) = logDebugParam(__PACKAGE__ . 'validate');\n"
|
|
"\n\n\n"
|
|
"foreach my $strFile ($self->keys(MANIFEST_SECTION_TARGET_FILE))\n"
|
|
"{\n"
|
|
"\n"
|
|
"if (!$self->test(MANIFEST_SECTION_TARGET_FILE, $strFile, MANIFEST_SUBKEY_SIZE))\n"
|
|
"{\n"
|
|
"confess &log(ASSERT, \"manifest subvalue 'size' not set for file '${strFile}'\");\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if ($self->numericGet(MANIFEST_SECTION_TARGET_FILE, $strFile, MANIFEST_SUBKEY_SIZE) > 0 &&\n"
|
|
"!$self->test(MANIFEST_SECTION_TARGET_FILE, $strFile, MANIFEST_SUBKEY_CHECKSUM))\n"
|
|
"{\n"
|
|
"confess &log(ASSERT, \"manifest subvalue 'checksum' not set for file '${strFile}'\");\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"return logDebugReturn($strOperation);\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub dbVersion\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n"
|
|
"return $self->get(MANIFEST_SECTION_BACKUP_DB, MANIFEST_KEY_DB_VERSION);\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub xactPath\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n"
|
|
"return $self->dbVersion() >= PG_VERSION_10 ? 'pg_xact' : 'pg_clog';\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub walPath\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n"
|
|
"return $self->dbVersion() >= PG_VERSION_10 ? 'pg_wal' : 'pg_xlog';\n"
|
|
"}\n"
|
|
"\n\n\n\n\n\n"
|
|
"sub isMasterFile\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"my $strFile = shift;\n"
|
|
"\n"
|
|
"return\n"
|
|
"$strFile !~ ('^(' . MANIFEST_TARGET_PGDATA . '\\/' . '(' . DB_PATH_BASE . '|' . DB_PATH_GLOBAL . '|' .\n"
|
|
"$self->xactPath() . '|' . DB_PATH_PGMULTIXACT . ')|' . DB_PATH_PGTBLSPC . ')\\/');\n"
|
|
"}\n"
|
|
"\n\n\n\n\n\n"
|
|
"sub isChecksumPage\n"
|
|
"{\n"
|
|
"my $strFile = shift;\n"
|
|
"\n"
|
|
"if (($strFile =~ ('^' . MANIFEST_TARGET_PGDATA . '\\/' . DB_PATH_BASE . '|^' . MANIFEST_TARGET_PGTBLSPC . '\\/') &&\n"
|
|
"$strFile !~ ('(' . DB_FILE_PGFILENODEMAP . '|' . DB_FILE_PGINTERNALINIT . '|' . DB_FILE_PGVERSION . ')$')) ||\n"
|
|
"($strFile =~ ('^' . MANIFEST_TARGET_PGDATA . '\\/' . DB_PATH_GLOBAL) &&\n"
|
|
"$strFile !~ ('(' . DB_FILE_PGFILENODEMAP . '|' . DB_FILE_PGINTERNALINIT . '|' . DB_FILE_PGVERSION . '|' .\n"
|
|
"DB_FILE_PGCONTROL . ')$')))\n"
|
|
"{\n"
|
|
"return true;\n"
|
|
"}\n"
|
|
"\n"
|
|
"return false;\n"
|
|
"}\n"
|
|
"\n"
|
|
"push @EXPORT, qw(isChecksumPage);\n"
|
|
"\n"
|
|
"1;\n"
|
|
},
|
|
{
|
|
.name = "pgBackRest/Protocol/Base/Master.pm",
|
|
.data =
|
|
"\n\n\n"
|
|
"package pgBackRest::Protocol::Base::Master;\n"
|
|
"\n"
|
|
"use strict;\n"
|
|
"use warnings FATAL => qw(all);\n"
|
|
"use Carp qw(confess);\n"
|
|
"use English '-no_match_vars';\n"
|
|
"\n"
|
|
"use Exporter qw(import);\n"
|
|
"our @EXPORT = qw();\n"
|
|
"use Time::HiRes qw(gettimeofday);\n"
|
|
"use JSON::PP;\n"
|
|
"\n"
|
|
"use pgBackRest::Common::Exception;\n"
|
|
"use pgBackRest::Common::Ini;\n"
|
|
"use pgBackRest::Common::Log;\n"
|
|
"use pgBackRest::Version;\n"
|
|
"\n\n\n\n"
|
|
"use constant OP_NOOP => 'noop';\n"
|
|
"push @EXPORT, qw(OP_NOOP);\n"
|
|
"use constant OP_EXIT => 'exit';\n"
|
|
"push @EXPORT, qw(OP_EXIT);\n"
|
|
"\n\n\n\n"
|
|
"sub new\n"
|
|
"{\n"
|
|
"my $class = shift;\n"
|
|
"\n\n"
|
|
"my $self = {};\n"
|
|
"bless $self, $class;\n"
|
|
"\n\n"
|
|
"(\n"
|
|
"my $strOperation,\n"
|
|
"$self->{strName},\n"
|
|
"$self->{strId},\n"
|
|
"$self->{oIo},\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->new', \\@_,\n"
|
|
"{name => 'strName', trace => true},\n"
|
|
"{name => 'strId', trace => true},\n"
|
|
"{name => 'oIo', trace => true},\n"
|
|
");\n"
|
|
"\n\n"
|
|
"$self->{oJSON} = JSON::PP->new()->allow_nonref();\n"
|
|
"\n\n"
|
|
"$self->{fKeepAliveTimeout} = $self->io()->timeout() / 2 > 120 ? 120 : $self->io()->timeout() / 2;\n"
|
|
"$self->{fKeepAliveTime} = gettimeofday();\n"
|
|
"\n\n"
|
|
"$self->{strErrorPrefix} = 'raised from ' . $self->{strId};\n"
|
|
"\n\n"
|
|
"$self->greetingRead();\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'self', value => $self, trace => true}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub DESTROY\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n"
|
|
"$self->close();\n"
|
|
"}\n"
|
|
"\n\n\n\n\n\n"
|
|
"sub greetingRead\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my $strGreeting = $self->io()->readLine(true);\n"
|
|
"\n\n"
|
|
"my $hGreeting;\n"
|
|
"\n"
|
|
"eval\n"
|
|
"{\n"
|
|
"$hGreeting = $self->{oJSON}->decode($strGreeting);\n"
|
|
"\n"
|
|
"return true;\n"
|
|
"}\n"
|
|
"\n"
|
|
"or do\n"
|
|
"{\n"
|
|
"$self->io()->error(ERROR_PROTOCOL, 'invalid protocol greeting', $strGreeting);\n"
|
|
"};\n"
|
|
"\n\n"
|
|
"for my $hParam ({strName => 'name', strExpected => PROJECT_NAME},\n"
|
|
"{strName => 'version', strExpected => PROJECT_VERSION},\n"
|
|
"{strName => 'service', strExpected => $self->{strName}})\n"
|
|
"{\n"
|
|
"if (!defined($hGreeting->{$hParam->{strName}}) || $hGreeting->{$hParam->{strName}} ne $hParam->{strExpected})\n"
|
|
"{\n"
|
|
"confess &log(ERROR,\n"
|
|
"'found name \\'' . (defined($hGreeting->{$hParam->{strName}}) ? $hGreeting->{$hParam->{strName}} : '[undef]') .\n"
|
|
"\"' in protocol greeting instead of expected '$hParam->{strExpected}'\", ERROR_HOST_CONNECT);\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"$self->noOp();\n"
|
|
"}\n"
|
|
"\n\n\n\n\n\n"
|
|
"sub outputRead\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$bOutputRequired,\n"
|
|
"$bSuppressLog,\n"
|
|
"$bWarnOnError,\n"
|
|
"$bRef,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->outputRead', \\@_,\n"
|
|
"{name => 'bOutputRequired', default => false, trace => true},\n"
|
|
"{name => 'bSuppressLog', required => false, trace => true},\n"
|
|
"{name => 'bWarnOnError', default => false, trace => true},\n"
|
|
"{name => 'bRef', default => false, trace => true},\n"
|
|
");\n"
|
|
"\n"
|
|
"my $strProtocolResult = $self->io()->readLine();\n"
|
|
"\n"
|
|
"logDebugMisc\n"
|
|
"(\n"
|
|
"$strOperation, undef,\n"
|
|
"{name => 'strProtocolResult', value => $strProtocolResult, trace => true}\n"
|
|
");\n"
|
|
"\n"
|
|
"my $hResult = $self->{oJSON}->decode($strProtocolResult);\n"
|
|
"\n\n"
|
|
"if (defined($hResult->{err}))\n"
|
|
"{\n"
|
|
"my $strError = $self->{strErrorPrefix} . (defined($hResult->{out}) ? \": $hResult->{out}\" : '');\n"
|
|
"\n\n"
|
|
"if (!$bWarnOnError)\n"
|
|
"{\n"
|
|
"confess &log(\n"
|
|
"ERROR, $strError . (defined($hResult->{errStack}) ? \"\\n$hResult->{errStack}\" : ''), $hResult->{err}, $bSuppressLog);\n"
|
|
"}\n"
|
|
"\n"
|
|
"&log(WARN, $strError, $hResult->{err});\n"
|
|
"undef($hResult->{out});\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"$self->{fKeepAliveTime} = gettimeofday();\n"
|
|
"\n\n"
|
|
"if ($bOutputRequired && !defined($hResult->{out}))\n"
|
|
"{\n"
|
|
"confess &log(ERROR, \"$self->{strErrorPrefix}: output is not defined\", ERROR_PROTOCOL_OUTPUT_REQUIRED);\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'hOutput', value => $hResult->{out}, ref => $bRef, trace => true}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n\n\n"
|
|
"sub cmdWrite\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$strCommand,\n"
|
|
"$hParam,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->cmdWrite', \\@_,\n"
|
|
"{name => 'strCommand', trace => true},\n"
|
|
"{name => 'hParam', required => false, trace => true},\n"
|
|
");\n"
|
|
"\n"
|
|
"my $strProtocolCommand = $self->{oJSON}->encode({cmd => $strCommand, param => $hParam});\n"
|
|
"\n"
|
|
"logDebugMisc\n"
|
|
"(\n"
|
|
"$strOperation, undef,\n"
|
|
"{name => 'strProtocolCommand', value => $strProtocolCommand, trace => true}\n"
|
|
");\n"
|
|
"\n\n"
|
|
"$self->io()->writeLine($strProtocolCommand);\n"
|
|
"\n\n"
|
|
"$self->{fKeepAliveTime} = gettimeofday();\n"
|
|
"\n\n"
|
|
"logDebugReturn($strOperation);\n"
|
|
"}\n"
|
|
"\n\n\n\n\n\n"
|
|
"sub cmdExecute\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"my $strCommand = shift;\n"
|
|
"my $oParamRef = shift;\n"
|
|
"my $bOutputRequired = shift;\n"
|
|
"my $bWarnOnError = shift;\n"
|
|
"\n"
|
|
"$self->cmdWrite($strCommand, $oParamRef);\n"
|
|
"\n"
|
|
"return $self->outputRead($bOutputRequired, undef, $bWarnOnError);\n"
|
|
"}\n"
|
|
"\n\n\n\n\n\n"
|
|
"sub keepAlive\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n"
|
|
"if (gettimeofday() - $self->{fKeepAliveTimeout} > $self->{fKeepAliveTime})\n"
|
|
"{\n"
|
|
"$self->noOp();\n"
|
|
"\n\n"
|
|
"&log(TEST, TEST_KEEP_ALIVE);\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n\n\n\n\n"
|
|
"sub noOp\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n"
|
|
"$self->cmdExecute(OP_NOOP, undef, false);\n"
|
|
"$self->{fKeepAliveTime} = gettimeofday();\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub io {shift->{oIo}}\n"
|
|
"sub master {true}\n"
|
|
"\n"
|
|
"1;\n"
|
|
},
|
|
{
|
|
.name = "pgBackRest/Protocol/Base/Minion.pm",
|
|
.data =
|
|
"\n\n\n"
|
|
"package pgBackRest::Protocol::Base::Minion;\n"
|
|
"\n"
|
|
"use strict;\n"
|
|
"use warnings FATAL => qw(all);\n"
|
|
"use Carp qw(confess);\n"
|
|
"use English '-no_match_vars';\n"
|
|
"\n"
|
|
"use Exporter qw(import);\n"
|
|
"our @EXPORT = qw();\n"
|
|
"use JSON::PP;\n"
|
|
"\n"
|
|
"use pgBackRest::Common::Exception;\n"
|
|
"use pgBackRest::Common::Ini;\n"
|
|
"use pgBackRest::Common::Lock;\n"
|
|
"use pgBackRest::Common::Log;\n"
|
|
"use pgBackRest::Common::String;\n"
|
|
"use pgBackRest::LibC qw(:lock);\n"
|
|
"use pgBackRest::Protocol::Base::Master;\n"
|
|
"use pgBackRest::Protocol::Helper;\n"
|
|
"use pgBackRest::Version;\n"
|
|
"\n\n\n\n"
|
|
"use constant OP_POST => 'post';\n"
|
|
"push @EXPORT, qw(OP_POST);\n"
|
|
"\n\n\n\n"
|
|
"sub new\n"
|
|
"{\n"
|
|
"my $class = shift;\n"
|
|
"\n\n"
|
|
"my $self = {};\n"
|
|
"bless $self, $class;\n"
|
|
"\n\n"
|
|
"(\n"
|
|
"my $strOperation,\n"
|
|
"$self->{strName},\n"
|
|
"$self->{oIo},\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->new', \\@_,\n"
|
|
"{name => 'strName', trace => true},\n"
|
|
"{name => 'oIo', trace => true},\n"
|
|
");\n"
|
|
"\n\n"
|
|
"$self->{oJSON} = JSON::PP->new()->allow_nonref();\n"
|
|
"\n\n"
|
|
"$self->greetingWrite();\n"
|
|
"\n\n"
|
|
"$self->{hCommandMap} = $self->can('init') ? $self->init() : undef;\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'self', value => $self, trace => true}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n\n\n"
|
|
"sub greetingWrite\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"$self->io()->writeLine((JSON::PP->new()->canonical()->allow_nonref())->encode(\n"
|
|
"{name => PROJECT_NAME, service => $self->{strName}, version => PROJECT_VERSION}));\n"
|
|
"}\n"
|
|
"\n\n\n\n\n\n"
|
|
"sub errorWrite\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"my $oException = shift;\n"
|
|
"\n\n"
|
|
"if (!isException(\\$oException))\n"
|
|
"{\n"
|
|
"confess &log(ERROR, 'unknown error: ' . $oException, ERROR_UNKNOWN);\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"$self->io()->writeLine($self->{oJSON}->encode({err => $oException->code(), out => $oException->message()}));\n"
|
|
"}\n"
|
|
"\n\n\n\n\n\n"
|
|
"sub outputWrite\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n"
|
|
"$self->io()->writeLine($self->{oJSON}->encode({out => \\@_}));\n"
|
|
"}\n"
|
|
"\n\n\n\n\n\n"
|
|
"sub cmdRead\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n"
|
|
"my $hCommand = $self->{oJSON}->decode($self->io()->readLine());\n"
|
|
"\n"
|
|
"return $hCommand->{cmd}, $hCommand->{param};\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub process\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"my $strLockPath = shift;\n"
|
|
"my $strLockCommand = shift;\n"
|
|
"my $strLockStanza = shift;\n"
|
|
"my $iProcessId = shift;\n"
|
|
"\n\n"
|
|
"logLevelSet(undef, undef, OFF);\n"
|
|
"\n\n\n"
|
|
"my $oPermanentError;\n"
|
|
"\n\n"
|
|
"eval\n"
|
|
"{\n"
|
|
"\n\n"
|
|
"if (defined($strLockPath) && defined($strLockStanza) && $iProcessId == 0)\n"
|
|
"{\n"
|
|
"eval\n"
|
|
"{\n"
|
|
"if (lockAcquire($strLockPath, $strLockCommand, $strLockStanza, 30, true))\n"
|
|
"{\n"
|
|
"\n"
|
|
"lockStopTest();\n"
|
|
"}\n"
|
|
"\n"
|
|
"return true;\n"
|
|
"}\n"
|
|
"or do\n"
|
|
"{\n"
|
|
"$oPermanentError = $EVAL_ERROR;\n"
|
|
"};\n"
|
|
"}\n"
|
|
"\n"
|
|
"while (true)\n"
|
|
"{\n"
|
|
"my ($strCommand, $rParam) = $self->cmdRead();\n"
|
|
"\n"
|
|
"last if ($strCommand eq OP_EXIT);\n"
|
|
"\n\n"
|
|
"if (defined($oPermanentError))\n"
|
|
"{\n"
|
|
"$self->errorWrite($oPermanentError);\n"
|
|
"}\n"
|
|
"\n"
|
|
"else\n"
|
|
"{\n"
|
|
"eval\n"
|
|
"{\n"
|
|
"\n"
|
|
"if (defined($self->{hCommandMap}{$strCommand}))\n"
|
|
"{\n"
|
|
"$self->outputWrite($self->{hCommandMap}{$strCommand}->($rParam));\n"
|
|
"}\n"
|
|
"\n"
|
|
"elsif ($strCommand eq OP_NOOP)\n"
|
|
"{\n"
|
|
"protocolKeepAlive();\n"
|
|
"$self->outputWrite();\n"
|
|
"}\n"
|
|
"else\n"
|
|
"{\n"
|
|
"confess \"invalid command: ${strCommand}\";\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if (defined($self->{hCommandMap}{&OP_POST}))\n"
|
|
"{\n"
|
|
"$self->{hCommandMap}{&OP_POST}->();\n"
|
|
"}\n"
|
|
"\n"
|
|
"return true;\n"
|
|
"}\n"
|
|
"\n"
|
|
"or do\n"
|
|
"{\n"
|
|
"$self->errorWrite($EVAL_ERROR);\n"
|
|
"};\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n"
|
|
"return true;\n"
|
|
"}\n"
|
|
"or do\n"
|
|
"{\n"
|
|
"my $oException = $EVAL_ERROR;\n"
|
|
"\n\n"
|
|
"logLevelSet(undef, undef, PROTOCOL);\n"
|
|
"\n\n"
|
|
"if (isException(\\$oException))\n"
|
|
"{\n"
|
|
"confess &log($oException->level(), $oException->message(), $oException->code());\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"confess &log(ERROR, 'unknown error: ' . $oException, ERROR_UNKNOWN);\n"
|
|
"};\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub io {shift->{oIo}}\n"
|
|
"sub master {false}\n"
|
|
"\n"
|
|
"1;\n"
|
|
},
|
|
{
|
|
.name = "pgBackRest/Protocol/Command/Master.pm",
|
|
.data =
|
|
"\n\n\n"
|
|
"package pgBackRest::Protocol::Command::Master;\n"
|
|
"use parent 'pgBackRest::Protocol::Base::Master';\n"
|
|
"\n"
|
|
"use strict;\n"
|
|
"use warnings FATAL => qw(all);\n"
|
|
"use Carp qw(confess);\n"
|
|
"use English '-no_match_vars';\n"
|
|
"\n"
|
|
"use File::Basename qw(dirname);\n"
|
|
"use Time::HiRes qw(gettimeofday);\n"
|
|
"\n"
|
|
"use pgBackRest::Common::Exception;\n"
|
|
"use pgBackRest::Common::Ini;\n"
|
|
"use pgBackRest::Common::Log;\n"
|
|
"use pgBackRest::Common::Io::Process;\n"
|
|
"use pgBackRest::Protocol::Base::Master;\n"
|
|
"use pgBackRest::Version;\n"
|
|
"\n\n\n\n"
|
|
"sub new\n"
|
|
"{\n"
|
|
"my $class = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$strName,\n"
|
|
"$strId,\n"
|
|
"$strCommand,\n"
|
|
"$iBufferMax,\n"
|
|
"$iCompressLevel,\n"
|
|
"$iCompressLevelNetwork,\n"
|
|
"$iProtocolTimeout,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->new', \\@_,\n"
|
|
"{name => 'strName'},\n"
|
|
"{name => 'strId'},\n"
|
|
"{name => 'strCommand'},\n"
|
|
"{name => 'iBufferMax'},\n"
|
|
"{name => 'iCompressLevel'},\n"
|
|
"{name => 'iCompressLevelNetwork'},\n"
|
|
"{name => 'iProtocolTimeout'},\n"
|
|
");\n"
|
|
"\n\n"
|
|
"if (!defined($strCommand))\n"
|
|
"{\n"
|
|
"confess &log(ASSERT, 'strCommand must be set');\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"my $oIo = new pgBackRest::Common::Io::Process(\n"
|
|
"new pgBackRest::Common::Io::Buffered(\n"
|
|
"new pgBackRest::Common::Io::Handle($strId), $iProtocolTimeout, $iBufferMax), $strCommand);\n"
|
|
"\n\n"
|
|
"my $self = $class->SUPER::new($strName, $strId, $oIo);\n"
|
|
"bless $self, $class;\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'self', value => $self}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub close\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$bComplete,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->close', \\@_,\n"
|
|
"{name => 'bComplete', default => false, trace => true},\n"
|
|
");\n"
|
|
"\n\n"
|
|
"my $iExitStatus = 0;\n"
|
|
"my $bClosed = false;\n"
|
|
"\n\n"
|
|
"if (defined($self->io()) && defined($self->io()->processId()))\n"
|
|
"{\n"
|
|
"&log(TRACE, \"sending exit command to process\");\n"
|
|
"\n"
|
|
"eval\n"
|
|
"{\n"
|
|
"$self->cmdWrite(OP_EXIT);\n"
|
|
"return true;\n"
|
|
"}\n"
|
|
"or do\n"
|
|
"{\n"
|
|
"my $oException = $EVAL_ERROR;\n"
|
|
"my $strError = 'unable to shutdown protocol';\n"
|
|
"my $strHint = 'HINT: the process completed all operations successfully but protocol-timeout may need to be increased.';\n"
|
|
"\n"
|
|
"if (isException(\\$oException))\n"
|
|
"{\n"
|
|
"$iExitStatus = $oException->code();\n"
|
|
"}\n"
|
|
"else\n"
|
|
"{\n"
|
|
"if (!defined($oException))\n"
|
|
"{\n"
|
|
"$oException = 'unknown error';\n"
|
|
"}\n"
|
|
"\n"
|
|
"$iExitStatus = ERROR_UNKNOWN;\n"
|
|
"}\n"
|
|
"\n"
|
|
"&log(WARN,\n"
|
|
"$strError . ($iExitStatus == ERROR_UNKNOWN ? '' : sprintf(' [%03d]', $oException->code())) . ': ' .\n"
|
|
"($iExitStatus == ERROR_UNKNOWN ? $oException : $oException->message()) .\n"
|
|
"($bComplete ? \"\\n${strHint}\" : ''));\n"
|
|
"};\n"
|
|
"\n"
|
|
"$self->{oIo}->close();\n"
|
|
"undef($self->{oIo});\n"
|
|
"$bClosed = true;\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'iExitStatus', value => $iExitStatus, trace => !$bClosed}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n"
|
|
"1;\n"
|
|
},
|
|
{
|
|
.name = "pgBackRest/Protocol/Command/Minion.pm",
|
|
.data =
|
|
"\n\n\n"
|
|
"package pgBackRest::Protocol::Command::Minion;\n"
|
|
"use parent 'pgBackRest::Protocol::Base::Minion';\n"
|
|
"\n"
|
|
"use strict;\n"
|
|
"use warnings FATAL => qw(all);\n"
|
|
"use Carp qw(confess);\n"
|
|
"use English '-no_match_vars';\n"
|
|
"\n"
|
|
"use JSON::PP;\n"
|
|
"\n"
|
|
"use pgBackRest::Common::Exception;\n"
|
|
"use pgBackRest::Common::Ini;\n"
|
|
"use pgBackRest::Common::Log;\n"
|
|
"use pgBackRest::Common::String;\n"
|
|
"use pgBackRest::Protocol::Base::Minion;\n"
|
|
"use pgBackRest::Common::Io::Buffered;\n"
|
|
"use pgBackRest::Version;\n"
|
|
"\n\n\n\n"
|
|
"sub new\n"
|
|
"{\n"
|
|
"my $class = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$strName,\n"
|
|
"$iBufferMax,\n"
|
|
"$iProtocolTimeout,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->new', \\@_,\n"
|
|
"{name => 'strName'},\n"
|
|
"{name => 'iBufferMax'},\n"
|
|
"{name => 'iProtocolTimeout'},\n"
|
|
");\n"
|
|
"\n\n"
|
|
"my $oIo =\n"
|
|
"new pgBackRest::Common::Io::Buffered(\n"
|
|
"new pgBackRest::Common::Io::Handle('stdio', *STDIN, *STDOUT), $iProtocolTimeout, $iBufferMax);\n"
|
|
"\n\n"
|
|
"my $self = $class->SUPER::new($strName, $oIo);\n"
|
|
"bless $self, $class;\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'self', value => $self}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n"
|
|
"1;\n"
|
|
},
|
|
{
|
|
.name = "pgBackRest/Protocol/Helper.pm",
|
|
.data =
|
|
"\n\n\n"
|
|
"package pgBackRest::Protocol::Helper;\n"
|
|
"\n"
|
|
"use strict;\n"
|
|
"use warnings FATAL => qw(all);\n"
|
|
"use Carp qw(confess);\n"
|
|
"\n"
|
|
"use Exporter qw(import);\n"
|
|
"our @EXPORT = qw();\n"
|
|
"\n"
|
|
"use pgBackRest::Common::Log;\n"
|
|
"use pgBackRest::Config::Config;\n"
|
|
"use pgBackRest::Protocol::Remote::Master;\n"
|
|
"use pgBackRest::Version;\n"
|
|
"\n\n\n\n\n"
|
|
"use constant OP_BACKUP_FILE => 'backupFile';\n"
|
|
"push @EXPORT, qw(OP_BACKUP_FILE);\n"
|
|
"\n\n"
|
|
"use constant OP_ARCHIVE_GET_CHECK => 'archiveCheck';\n"
|
|
"push @EXPORT, qw(OP_ARCHIVE_GET_CHECK);\n"
|
|
"\n\n"
|
|
"use constant OP_CHECK_BACKUP_INFO_CHECK => 'backupInfoCheck';\n"
|
|
"push @EXPORT, qw(OP_CHECK_BACKUP_INFO_CHECK);\n"
|
|
"\n\n"
|
|
"use constant OP_DB_CONNECT => 'dbConnect';\n"
|
|
"push @EXPORT, qw(OP_DB_CONNECT);\n"
|
|
"use constant OP_DB_EXECUTE_SQL => 'dbExecSql';\n"
|
|
"push @EXPORT, qw(OP_DB_EXECUTE_SQL);\n"
|
|
"use constant OP_DB_INFO => 'dbInfo';\n"
|
|
"push @EXPORT, qw(OP_DB_INFO);\n"
|
|
"\n\n"
|
|
"use constant OP_STORAGE_OPEN_READ => 'storageOpenRead';\n"
|
|
"push @EXPORT, qw(OP_STORAGE_OPEN_READ);\n"
|
|
"use constant OP_STORAGE_OPEN_WRITE => 'storageOpenWrite';\n"
|
|
"push @EXPORT, qw(OP_STORAGE_OPEN_WRITE);\n"
|
|
"use constant OP_STORAGE_CIPHER_PASS_USER => 'storageCipherPassUser';\n"
|
|
"push @EXPORT, qw(OP_STORAGE_CIPHER_PASS_USER);\n"
|
|
"use constant OP_STORAGE_EXISTS => 'storageExists';\n"
|
|
"push @EXPORT, qw(OP_STORAGE_EXISTS);\n"
|
|
"use constant OP_STORAGE_HASH_SIZE => 'storageHashSize';\n"
|
|
"push @EXPORT, qw(OP_STORAGE_HASH_SIZE);\n"
|
|
"use constant OP_STORAGE_LIST => 'storageList';\n"
|
|
"push @EXPORT, qw(OP_STORAGE_LIST);\n"
|
|
"use constant OP_STORAGE_MANIFEST => 'storageManifest';\n"
|
|
"push @EXPORT, qw(OP_STORAGE_MANIFEST);\n"
|
|
"use constant OP_STORAGE_MOVE => 'storageMove';\n"
|
|
"push @EXPORT, qw(OP_STORAGE_MOVE);\n"
|
|
"use constant OP_STORAGE_PATH_GET => 'storagePathGet';\n"
|
|
"push @EXPORT, qw(OP_STORAGE_PATH_GET);\n"
|
|
"\n\n"
|
|
"use constant OP_RESTORE_FILE => 'restoreFile';\n"
|
|
"push @EXPORT, qw(OP_RESTORE_FILE);\n"
|
|
"\n\n"
|
|
"use constant OP_WAIT => 'wait';\n"
|
|
"push @EXPORT, qw(OP_WAIT);\n"
|
|
"\n\n\n\n"
|
|
"my $hProtocol = {};\n"
|
|
"\n\n\n\n\n\n"
|
|
"sub isRepoLocal\n"
|
|
"{\n"
|
|
"\n"
|
|
"if (cfgCommandTest(CFGCMD_REMOTE) && !cfgOptionTest(CFGOPT_TYPE, CFGOPTVAL_REMOTE_TYPE_BACKUP))\n"
|
|
"{\n"
|
|
"confess &log(ASSERT, 'isRepoLocal() not valid on ' . cfgOption(CFGOPT_TYPE) . ' remote');\n"
|
|
"}\n"
|
|
"\n"
|
|
"return cfgOptionTest(CFGOPT_REPO_HOST) ? false : true;\n"
|
|
"}\n"
|
|
"\n"
|
|
"push @EXPORT, qw(isRepoLocal);\n"
|
|
"\n\n\n\n"
|
|
"sub isDbLocal\n"
|
|
"{\n"
|
|
"\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$iRemoteIdx,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '::isDbLocal', \\@_,\n"
|
|
"{name => 'iRemoteIdx', optional => true, default => cfgOptionValid(CFGOPT_HOST_ID) ? cfgOption(CFGOPT_HOST_ID) : 1,\n"
|
|
"trace => true},\n"
|
|
");\n"
|
|
"\n\n"
|
|
"if (cfgCommandTest(CFGCMD_REMOTE) && !cfgOptionTest(CFGOPT_TYPE, CFGOPTVAL_REMOTE_TYPE_DB))\n"
|
|
"{\n"
|
|
"confess &log(ASSERT, 'isDbLocal() not valid on ' . cfgOption(CFGOPT_TYPE) . ' remote');\n"
|
|
"}\n"
|
|
"\n"
|
|
"my $bLocal = cfgOptionTest(cfgOptionIdFromIndex(CFGOPT_PG_HOST, $iRemoteIdx)) ? false : true;\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'bLocal', value => $bLocal, trace => true}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n"
|
|
"push @EXPORT, qw(isDbLocal);\n"
|
|
"\n\n\n\n"
|
|
"sub protocolParam\n"
|
|
"{\n"
|
|
"\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$strCommand,\n"
|
|
"$strRemoteType,\n"
|
|
"$iRemoteIdx,\n"
|
|
"$strBackRestBin,\n"
|
|
"$iProcessIdx,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '::protocolParam', \\@_,\n"
|
|
"{name => 'strCommand'},\n"
|
|
"{name => 'strRemoteType'},\n"
|
|
"{name => 'iRemoteIdx', default => cfgOptionValid(CFGOPT_HOST_ID) ? cfgOption(CFGOPT_HOST_ID) : 1},\n"
|
|
"{name => 'strBackRestBin', optional => true},\n"
|
|
"{name => 'iProcessIdx', optional => true,\n"
|
|
"default => cfgOptionValid(CFGOPT_PROCESS) ? cfgOption(CFGOPT_PROCESS, false) : undef},\n"
|
|
");\n"
|
|
"\n\n"
|
|
"my $iOptionIdCmd = CFGOPT_REPO_HOST_CMD;\n"
|
|
"my $iOptionIdConfig = CFGOPT_REPO_HOST_CONFIG;\n"
|
|
"my $iOptionIdConfigIncludePath = CFGOPT_REPO_HOST_CONFIG_INCLUDE_PATH;\n"
|
|
"my $iOptionIdConfigPath = CFGOPT_REPO_HOST_CONFIG_PATH;\n"
|
|
"my $iOptionIdHost = CFGOPT_REPO_HOST;\n"
|
|
"my $iOptionIdUser = CFGOPT_REPO_HOST_USER;\n"
|
|
"my $strOptionDbPath = undef;\n"
|
|
"my $strOptionDbPort = undef;\n"
|
|
"my $strOptionDbSocketPath = undef;\n"
|
|
"my $strOptionSshPort = CFGOPT_REPO_HOST_PORT;\n"
|
|
"\n"
|
|
"if ($strRemoteType eq CFGOPTVAL_REMOTE_TYPE_DB)\n"
|
|
"{\n"
|
|
"$iOptionIdCmd = cfgOptionIdFromIndex(CFGOPT_PG_HOST_CMD, $iRemoteIdx);\n"
|
|
"$iOptionIdConfig = cfgOptionIdFromIndex(CFGOPT_PG_HOST_CONFIG, $iRemoteIdx);\n"
|
|
"$iOptionIdConfigIncludePath = cfgOptionIdFromIndex(CFGOPT_PG_HOST_CONFIG_INCLUDE_PATH, $iRemoteIdx);\n"
|
|
"$iOptionIdConfigPath = cfgOptionIdFromIndex(CFGOPT_PG_HOST_CONFIG_PATH, $iRemoteIdx);\n"
|
|
"$iOptionIdHost = cfgOptionIdFromIndex(CFGOPT_PG_HOST, $iRemoteIdx);\n"
|
|
"$iOptionIdUser = cfgOptionIdFromIndex(CFGOPT_PG_HOST_USER, $iRemoteIdx);\n"
|
|
"$strOptionSshPort = cfgOptionIdFromIndex(CFGOPT_PG_HOST_PORT, $iRemoteIdx);\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if (cfgOptionValid(cfgOptionIdFromIndex(CFGOPT_PG_PATH, $iRemoteIdx)))\n"
|
|
"{\n"
|
|
"$strOptionDbPath =\n"
|
|
"cfgOptionSource(cfgOptionIdFromIndex(CFGOPT_PG_PATH, $iRemoteIdx)) eq CFGDEF_SOURCE_DEFAULT ?\n"
|
|
"undef : cfgOption(cfgOptionIdFromIndex(CFGOPT_PG_PATH, $iRemoteIdx));\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if (cfgOptionValid(cfgOptionIdFromIndex(CFGOPT_PG_PORT, $iRemoteIdx)))\n"
|
|
"{\n"
|
|
"$strOptionDbPort =\n"
|
|
"cfgOptionSource(cfgOptionIdFromIndex(CFGOPT_PG_PORT, $iRemoteIdx)) eq CFGDEF_SOURCE_DEFAULT ?\n"
|
|
"undef : cfgOption(cfgOptionIdFromIndex(CFGOPT_PG_PORT, $iRemoteIdx));\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if (cfgOptionValid(cfgOptionIdFromIndex(CFGOPT_PG_SOCKET_PATH, $iRemoteIdx)))\n"
|
|
"{\n"
|
|
"$strOptionDbSocketPath =\n"
|
|
"cfgOptionSource(cfgOptionIdFromIndex(CFGOPT_PG_SOCKET_PATH, $iRemoteIdx)) eq CFGDEF_SOURCE_DEFAULT ?\n"
|
|
"undef : cfgOption(cfgOptionIdFromIndex(CFGOPT_PG_SOCKET_PATH, $iRemoteIdx));\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"my $rhCommandOption =\n"
|
|
"{\n"
|
|
"&CFGOPT_COMMAND => {value => $strCommand},\n"
|
|
"&CFGOPT_PROCESS => {value => defined($iProcessIdx) ? $iProcessIdx : 0},\n"
|
|
"&CFGOPT_CONFIG =>\n"
|
|
"{value => cfgOptionValid($iOptionIdConfig) && cfgOptionSource($iOptionIdConfig) eq CFGDEF_SOURCE_DEFAULT ?\n"
|
|
"undef : cfgOption($iOptionIdConfig)},\n"
|
|
"&CFGOPT_CONFIG_INCLUDE_PATH =>\n"
|
|
"{value => cfgOptionValid($iOptionIdConfigIncludePath) &&\n"
|
|
"cfgOptionSource($iOptionIdConfigIncludePath) eq CFGDEF_SOURCE_DEFAULT ?\n"
|
|
"undef : cfgOption($iOptionIdConfigIncludePath)},\n"
|
|
"&CFGOPT_CONFIG_PATH =>\n"
|
|
"{value => cfgOptionValid($iOptionIdConfigPath) && cfgOptionSource($iOptionIdConfigPath) eq CFGDEF_SOURCE_DEFAULT ?\n"
|
|
"undef : cfgOption($iOptionIdConfigPath)},\n"
|
|
"&CFGOPT_TYPE => {value => $strRemoteType},\n"
|
|
"&CFGOPT_LOG_PATH => {},\n"
|
|
"&CFGOPT_LOCK_PATH => {},\n"
|
|
"\n\n"
|
|
"&CFGOPT_LOG_LEVEL_FILE => {value => cfgOption(CFGOPT_LOG_SUBPROCESS) ? cfgOption(CFGOPT_LOG_LEVEL_FILE) : lc(OFF)},\n"
|
|
"\n\n\n"
|
|
"&CFGOPT_LOG_LEVEL_STDERR => {},\n"
|
|
"\n"
|
|
"cfgOptionIdFromIndex(CFGOPT_PG_PATH, 1) => {value => $strOptionDbPath},\n"
|
|
"cfgOptionIdFromIndex(CFGOPT_PG_PORT, 1) => {value => $strOptionDbPort},\n"
|
|
"cfgOptionIdFromIndex(CFGOPT_PG_SOCKET_PATH, 1) => {value => $strOptionDbSocketPath},\n"
|
|
"\n\n"
|
|
"&CFGOPT_BUFFER_SIZE => {value => cfgOption(CFGOPT_BUFFER_SIZE)},\n"
|
|
"&CFGOPT_COMPRESS_LEVEL => {value => cfgOption(CFGOPT_COMPRESS_LEVEL)},\n"
|
|
"&CFGOPT_COMPRESS_LEVEL_NETWORK => {value => cfgOption(CFGOPT_COMPRESS_LEVEL_NETWORK)},\n"
|
|
"&CFGOPT_PROTOCOL_TIMEOUT => {value => cfgOption(CFGOPT_PROTOCOL_TIMEOUT)}\n"
|
|
"};\n"
|
|
"\n\n\n"
|
|
"for (my $iOptionIdx = 1; $iOptionIdx <= cfgOptionIndexTotal(CFGOPT_PG_HOST); $iOptionIdx++)\n"
|
|
"{\n"
|
|
"if ($iOptionIdx != 1)\n"
|
|
"{\n"
|
|
"$rhCommandOption->{cfgOptionIdFromIndex(CFGOPT_PG_HOST_CONFIG, $iOptionIdx)} = {};\n"
|
|
"$rhCommandOption->{cfgOptionIdFromIndex(CFGOPT_PG_HOST_CONFIG_INCLUDE_PATH, $iOptionIdx)} = {};\n"
|
|
"$rhCommandOption->{cfgOptionIdFromIndex(CFGOPT_PG_HOST_CONFIG_PATH, $iOptionIdx)} = {};\n"
|
|
"$rhCommandOption->{cfgOptionIdFromIndex(CFGOPT_PG_HOST, $iOptionIdx)} = {};\n"
|
|
"$rhCommandOption->{cfgOptionIdFromIndex(CFGOPT_PG_PATH, $iOptionIdx)} = {};\n"
|
|
"$rhCommandOption->{cfgOptionIdFromIndex(CFGOPT_PG_PORT, $iOptionIdx)} = {};\n"
|
|
"$rhCommandOption->{cfgOptionIdFromIndex(CFGOPT_PG_SOCKET_PATH, $iOptionIdx)} = {};\n"
|
|
"}\n"
|
|
"\n"
|
|
"$rhCommandOption->{cfgOptionIdFromIndex(CFGOPT_PG_HOST_CMD, $iOptionIdx)} = {};\n"
|
|
"$rhCommandOption->{cfgOptionIdFromIndex(CFGOPT_PG_HOST_USER, $iOptionIdx)} = {};\n"
|
|
"$rhCommandOption->{cfgOptionIdFromIndex(CFGOPT_PG_HOST_PORT, $iOptionIdx)} = {};\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"my $strRemoteCommand = cfgCommandWrite(\n"
|
|
"CFGCMD_REMOTE, true, defined($strBackRestBin) ? $strBackRestBin : cfgOption($iOptionIdCmd), undef, $rhCommandOption);\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'strRemoteHost', value => cfgOption($iOptionIdHost)},\n"
|
|
"{name => 'strRemoteHostUser', value => cfgOption($iOptionIdUser)},\n"
|
|
"{name => 'strRemoteHostSshPort', value => cfgOption($strOptionSshPort, false)},\n"
|
|
"{name => 'strRemoteCommand', value => $strRemoteCommand},\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n\n\n\n"
|
|
"sub protocolGet\n"
|
|
"{\n"
|
|
"\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$strRemoteType,\n"
|
|
"$iRemoteIdx,\n"
|
|
"$bCache,\n"
|
|
"$strBackRestBin,\n"
|
|
"$iProcessIdx,\n"
|
|
"$strCommand,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '::protocolGet', \\@_,\n"
|
|
"{name => 'strRemoteType'},\n"
|
|
"{name => 'iRemoteIdx', default => cfgOptionValid(CFGOPT_HOST_ID) ? cfgOption(CFGOPT_HOST_ID) : 1},\n"
|
|
"{name => 'bCache', optional => true, default => true},\n"
|
|
"{name => 'strBackRestBin', optional => true},\n"
|
|
"{name => 'iProcessIdx', optional => true},\n"
|
|
"{name => 'strCommand', optional => true,\n"
|
|
"default => cfgOptionValid(CFGOPT_COMMAND) ? cfgOption(CFGOPT_COMMAND) : cfgCommandName(cfgCommandGet())},\n"
|
|
");\n"
|
|
"\n\n"
|
|
"my $oProtocol;\n"
|
|
"\n\n"
|
|
"if (!cfgOptionTest(\n"
|
|
"cfgOptionIdFromIndex($strRemoteType eq CFGOPTVAL_REMOTE_TYPE_DB ? CFGOPT_PG_HOST : CFGOPT_REPO_HOST, $iRemoteIdx)))\n"
|
|
"{\n"
|
|
"confess &log(ASSERT, 'protocol cannot be created when remote host is not specified');\n"
|
|
"}\n"
|
|
"\n"
|
|
"else\n"
|
|
"{\n"
|
|
"\n"
|
|
"$oProtocol =\n"
|
|
"$bCache && defined($$hProtocol{$strRemoteType}{$iRemoteIdx}) ? $$hProtocol{$strRemoteType}{$iRemoteIdx} : undef;\n"
|
|
"\n"
|
|
"if ($bCache && $$hProtocol{$strRemoteType}{$iRemoteIdx})\n"
|
|
"{\n"
|
|
"$oProtocol = $$hProtocol{$strRemoteType}{$iRemoteIdx};\n"
|
|
"logDebugMisc($strOperation, 'found cached protocol');\n"
|
|
"\n\n\n"
|
|
"$oProtocol->noOp();\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if (!defined($oProtocol))\n"
|
|
"{\n"
|
|
"logDebugMisc($strOperation, 'create (' . ($bCache ? '' : 'un') . 'cached) remote protocol');\n"
|
|
"\n"
|
|
"my ($strRemoteHost, $strRemoteHostUser, $strRemoteHostSshPort, $strRemoteCommand) = protocolParam(\n"
|
|
"$strCommand, $strRemoteType, $iRemoteIdx, {strBackRestBin => $strBackRestBin, iProcessIdx => $iProcessIdx});\n"
|
|
"\n"
|
|
"$oProtocol = new pgBackRest::Protocol::Remote::Master\n"
|
|
"(\n"
|
|
"cfgOption(CFGOPT_CMD_SSH),\n"
|
|
"$strRemoteCommand,\n"
|
|
"cfgOption(CFGOPT_BUFFER_SIZE),\n"
|
|
"cfgOption(CFGOPT_COMPRESS_LEVEL),\n"
|
|
"cfgOption(CFGOPT_COMPRESS_LEVEL_NETWORK),\n"
|
|
"$strRemoteHost,\n"
|
|
"$strRemoteHostUser,\n"
|
|
"$strRemoteHostSshPort,\n"
|
|
"cfgOption(CFGOPT_PROTOCOL_TIMEOUT)\n"
|
|
");\n"
|
|
"\n\n"
|
|
"if ($bCache)\n"
|
|
"{\n"
|
|
"$$hProtocol{$strRemoteType}{$iRemoteIdx} = $oProtocol;\n"
|
|
"}\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'oProtocol', value => $oProtocol, trace => true}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n"
|
|
"push @EXPORT, qw(protocolGet);\n"
|
|
"\n\n\n\n"
|
|
"sub protocolList\n"
|
|
"{\n"
|
|
"\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$strRemoteType,\n"
|
|
"$iRemoteIdx,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '::protocolList', \\@_,\n"
|
|
"{name => 'strRemoteType', required => false, trace => true},\n"
|
|
"{name => 'iRemoteIdx', required => false, trace => true},\n"
|
|
");\n"
|
|
"\n"
|
|
"my @oyProtocol;\n"
|
|
"\n"
|
|
"if (!defined($strRemoteType))\n"
|
|
"{\n"
|
|
"foreach my $strRemoteType (sort(keys(%{$hProtocol})))\n"
|
|
"{\n"
|
|
"push(@oyProtocol, protocolList($strRemoteType));\n"
|
|
"}\n"
|
|
"}\n"
|
|
"elsif (!defined($iRemoteIdx))\n"
|
|
"{\n"
|
|
"foreach my $iRemoteIdx (sort(keys(%{$hProtocol->{$strRemoteType}})))\n"
|
|
"{\n"
|
|
"push(@oyProtocol, protocolList($strRemoteType, $iRemoteIdx));\n"
|
|
"}\n"
|
|
"}\n"
|
|
"elsif (defined($hProtocol->{$strRemoteType}{$iRemoteIdx}))\n"
|
|
"{\n"
|
|
"push(@oyProtocol, {strRemoteType => $strRemoteType, iRemoteIdx => $iRemoteIdx});\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'oyProtocol', value => \\@oyProtocol, trace => true}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n\n\n"
|
|
"sub protocolDestroy\n"
|
|
"{\n"
|
|
"\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$strRemoteType,\n"
|
|
"$iRemoteIdx,\n"
|
|
"$bComplete,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '::protocolDestroy', \\@_,\n"
|
|
"{name => 'strRemoteType', required => false},\n"
|
|
"{name => 'iRemoteIdx', required => false},\n"
|
|
"{name => 'bComplete', default => false},\n"
|
|
");\n"
|
|
"\n"
|
|
"my $iExitStatus = 0;\n"
|
|
"\n"
|
|
"foreach my $rhProtocol (protocolList($strRemoteType, $iRemoteIdx))\n"
|
|
"{\n"
|
|
"logDebugMisc(\n"
|
|
"$strOperation, 'found cached protocol',\n"
|
|
"{name => 'strRemoteType', value => $rhProtocol->{strRemoteType}},\n"
|
|
"{name => 'iRemoteIdx', value => $rhProtocol->{iRemoteIdx}});\n"
|
|
"\n"
|
|
"$iExitStatus = $hProtocol->{$rhProtocol->{strRemoteType}}{$rhProtocol->{iRemoteIdx}}->close($bComplete);\n"
|
|
"delete($hProtocol->{$rhProtocol->{strRemoteType}}{$rhProtocol->{iRemoteIdx}});\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'iExitStatus', value => $iExitStatus}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n"
|
|
"push @EXPORT, qw(protocolDestroy);\n"
|
|
"\n\n\n\n"
|
|
"sub protocolKeepAlive\n"
|
|
"{\n"
|
|
"\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$strRemoteType,\n"
|
|
"$iRemoteIdx,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '::protocolDestroy', \\@_,\n"
|
|
"{name => 'strRemoteType', required => false, trace => true},\n"
|
|
"{name => 'iRemoteIdx', required => false, trace => true},\n"
|
|
");\n"
|
|
"\n"
|
|
"foreach my $rhProtocol (protocolList($strRemoteType, $iRemoteIdx))\n"
|
|
"{\n"
|
|
"$hProtocol->{$rhProtocol->{strRemoteType}}{$rhProtocol->{iRemoteIdx}}->keepAlive();\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"return logDebugReturn($strOperation);\n"
|
|
"}\n"
|
|
"\n"
|
|
"push @EXPORT, qw(protocolKeepAlive);\n"
|
|
"\n"
|
|
"1;\n"
|
|
},
|
|
{
|
|
.name = "pgBackRest/Protocol/Local/Master.pm",
|
|
.data =
|
|
"\n\n\n"
|
|
"package pgBackRest::Protocol::Local::Master;\n"
|
|
"use parent 'pgBackRest::Protocol::Command::Master';\n"
|
|
"\n"
|
|
"use strict;\n"
|
|
"use warnings FATAL => qw(all);\n"
|
|
"use Carp qw(confess);\n"
|
|
"\n"
|
|
"use pgBackRest::Backup::File;\n"
|
|
"use pgBackRest::Common::Log;\n"
|
|
"use pgBackRest::Config::Config;\n"
|
|
"use pgBackRest::Protocol::Command::Master;\n"
|
|
"\n\n\n\n"
|
|
"sub new\n"
|
|
"{\n"
|
|
"my $class = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$strCommand,\n"
|
|
"$iProcessIdx,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->new', \\@_,\n"
|
|
"{name => 'strCommand'},\n"
|
|
"{name => 'iProcessIdx', default => 1},\n"
|
|
");\n"
|
|
"\n\n"
|
|
"my $self = $class->SUPER::new(\n"
|
|
"'local', \"local-${iProcessIdx} process\", $strCommand, cfgOption(CFGOPT_BUFFER_SIZE),\n"
|
|
"cfgOption(CFGOPT_COMPRESS_LEVEL), cfgOption(CFGOPT_COMPRESS_LEVEL_NETWORK), cfgOption(CFGOPT_PROTOCOL_TIMEOUT));\n"
|
|
"\n"
|
|
"bless $self, $class;\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'self', value => $self}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n"
|
|
"1;\n"
|
|
},
|
|
{
|
|
.name = "pgBackRest/Protocol/Local/Minion.pm",
|
|
.data =
|
|
"\n\n\n"
|
|
"package pgBackRest::Protocol::Local::Minion;\n"
|
|
"use parent 'pgBackRest::Protocol::Command::Minion';\n"
|
|
"\n"
|
|
"use strict;\n"
|
|
"use warnings FATAL => qw(all);\n"
|
|
"use Carp qw(confess);\n"
|
|
"\n"
|
|
"use pgBackRest::Backup::File;\n"
|
|
"use pgBackRest::Common::Log;\n"
|
|
"use pgBackRest::Config::Config;\n"
|
|
"use pgBackRest::Storage::Local;\n"
|
|
"use pgBackRest::Protocol::Base::Master;\n"
|
|
"use pgBackRest::Protocol::Base::Minion;\n"
|
|
"use pgBackRest::Protocol::Command::Minion;\n"
|
|
"use pgBackRest::Protocol::Helper;\n"
|
|
"use pgBackRest::RestoreFile;\n"
|
|
"\n\n\n\n"
|
|
"sub new\n"
|
|
"{\n"
|
|
"my $class = shift;\n"
|
|
"\n\n"
|
|
"my ($strOperation) = logDebugParam(__PACKAGE__ . '->new');\n"
|
|
"\n\n"
|
|
"my $self = $class->SUPER::new(cfgCommandName(CFGCMD_LOCAL), cfgOption(CFGOPT_BUFFER_SIZE), cfgOption(CFGOPT_PROTOCOL_TIMEOUT));\n"
|
|
"bless $self, $class;\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'self', value => $self}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub init\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my ($strOperation) = logDebugParam(__PACKAGE__ . '->init');\n"
|
|
"\n\n"
|
|
"my $hCommandMap =\n"
|
|
"{\n"
|
|
"&OP_BACKUP_FILE => sub {backupFile(@{shift()})},\n"
|
|
"\n\n"
|
|
"&OP_POST => sub {protocolKeepAlive()},\n"
|
|
"};\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'hCommandMap', value => $hCommandMap}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n"
|
|
"1;\n"
|
|
},
|
|
{
|
|
.name = "pgBackRest/Protocol/Local/Process.pm",
|
|
.data =
|
|
"\n\n\n\n\n"
|
|
"package pgBackRest::Protocol::Local::Process;\n"
|
|
"\n"
|
|
"use strict;\n"
|
|
"use warnings FATAL => qw(all);\n"
|
|
"use Carp qw(confess);\n"
|
|
"use English '-no_match_vars';\n"
|
|
"\n"
|
|
"use IO::Select;\n"
|
|
"\n"
|
|
"use pgBackRest::Common::Exception;\n"
|
|
"use pgBackRest::Common::Log;\n"
|
|
"use pgBackRest::Config::Config;\n"
|
|
"use pgBackRest::Protocol::Local::Master;\n"
|
|
"use pgBackRest::Version;\n"
|
|
"\n\n\n\n"
|
|
"sub new\n"
|
|
"{\n"
|
|
"my $class = shift;\n"
|
|
"\n"
|
|
"my $self = {};\n"
|
|
"bless $self, $class;\n"
|
|
"\n\n"
|
|
"(\n"
|
|
"my $strOperation,\n"
|
|
"$self->{strHostType},\n"
|
|
"$self->{iSelectTimeout},\n"
|
|
"$self->{strBackRestBin},\n"
|
|
"$self->{bConfessError},\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->new', \\@_,\n"
|
|
"{name => 'strHostType'},\n"
|
|
"{name => 'iSelectTimeout', default => int(cfgOption(CFGOPT_PROTOCOL_TIMEOUT) / 2)},\n"
|
|
"{name => 'strBackRestBin', default => projectBin()},\n"
|
|
"{name => 'bConfessError', default => true},\n"
|
|
");\n"
|
|
"\n\n"
|
|
"$self->{hHostMap} = {};\n"
|
|
"$self->{hyHost} = undef;\n"
|
|
"\n\n"
|
|
"$self->reset();\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'self', value => $self}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n\n\n"
|
|
"sub reset\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my ($strOperation) = logDebugParam(__PACKAGE__ . '->reset');\n"
|
|
"\n\n"
|
|
"$self->{oSelect} = undef;\n"
|
|
"\n\n"
|
|
"$self->{hyLocalMap} = undef;\n"
|
|
"$self->{hyLocal} = undef;\n"
|
|
"\n\n"
|
|
"$self->{bProcessing} = false;\n"
|
|
"\n\n"
|
|
"$self->{iQueued} = 0;\n"
|
|
"\n\n"
|
|
"$self->{iRunning} = 0;\n"
|
|
"\n\n"
|
|
"return logDebugReturn($strOperation);\n"
|
|
"}\n"
|
|
"\n\n\n\n\n\n"
|
|
"sub hostAdd\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$iHostConfigIdx,\n"
|
|
"$iProcessMax,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->hostAdd', \\@_,\n"
|
|
"{name => 'iHostConfigIdx'},\n"
|
|
"{name => 'iProcessMax'},\n"
|
|
");\n"
|
|
"\n"
|
|
"my $iHostIdx = $self->{hHostMap}{$iHostConfigIdx};\n"
|
|
"\n"
|
|
"if (!defined($iHostIdx))\n"
|
|
"{\n"
|
|
"$iHostIdx = defined($self->{hyHost}) ? @{$self->{hyHost}} : 0;\n"
|
|
"$self->{hHostMap}{$iHostConfigIdx} = $iHostIdx;\n"
|
|
"}\n"
|
|
"\n"
|
|
"my $hHost =\n"
|
|
"{\n"
|
|
"iHostConfigIdx => $iHostConfigIdx,\n"
|
|
"iProcessMax => $iProcessMax,\n"
|
|
"};\n"
|
|
"\n"
|
|
"push(@{$self->{hyHost}}, $hHost);\n"
|
|
"\n\n"
|
|
"return logDebugReturn($strOperation);\n"
|
|
"}\n"
|
|
"\n\n\n\n\n\n"
|
|
"sub hostConnect\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my ($strOperation) = logDebugParam(__PACKAGE__ . '->hostConnect');\n"
|
|
"\n\n"
|
|
"$self->{oSelect} = IO::Select->new();\n"
|
|
"\n\n"
|
|
"my $iHostIdx = 0;\n"
|
|
"\n"
|
|
"foreach my $hHost (@{$self->{hyHost}})\n"
|
|
"{\n"
|
|
"\n"
|
|
"if (!defined($hHost->{hyQueue}))\n"
|
|
"{\n"
|
|
"logDebugMisc(\n"
|
|
"$strOperation, \"no jobs for host\",\n"
|
|
"{name => 'strHostType', value => $self->{strHostType}},\n"
|
|
"{name => 'iHostConfigIdx', value => $hHost->{iHostConfigIdx}});\n"
|
|
"next;\n"
|
|
"}\n"
|
|
"\n"
|
|
"for (my $iHostProcessIdx = 0; $iHostProcessIdx < $hHost->{iProcessMax}; $iHostProcessIdx++)\n"
|
|
"{\n"
|
|
"my $iLocalIdx = defined($self->{hyLocal}) ? @{$self->{hyLocal}} : 0;\n"
|
|
"my $iProcessId = $iLocalIdx + 1;\n"
|
|
"\n"
|
|
"logDebugMisc(\n"
|
|
"$strOperation, 'start local process',\n"
|
|
"{name => 'strHostType', value => $self->{strHostType}},\n"
|
|
"{name => 'iHostProcessIdx', value => $iHostProcessIdx},\n"
|
|
"{name => 'iHostConfigIdx', value => $hHost->{iHostConfigIdx}},\n"
|
|
"{name => 'iHostIdx', value => $iHostIdx},\n"
|
|
"{name => 'iProcessId', value => $iProcessId});\n"
|
|
"\n"
|
|
"my $oLocal = new pgBackRest::Protocol::Local::Master\n"
|
|
"(\n"
|
|
"cfgCommandWrite(\n"
|
|
"CFGCMD_LOCAL, true, $self->{strBackRestBin}, undef,\n"
|
|
"{\n"
|
|
"&CFGOPT_COMMAND => {value => cfgCommandName(cfgCommandGet())},\n"
|
|
"&CFGOPT_PROCESS => {value => $iProcessId},\n"
|
|
"&CFGOPT_TYPE => {value => $self->{strHostType}},\n"
|
|
"&CFGOPT_HOST_ID => {value => $hHost->{iHostConfigIdx}},\n"
|
|
"\n\n"
|
|
"&CFGOPT_LOG_LEVEL_FILE =>\n"
|
|
"{value => cfgOption(CFGOPT_LOG_SUBPROCESS) ? cfgOption(CFGOPT_LOG_LEVEL_FILE) : lc(OFF)},\n"
|
|
"&CFGOPT_LOG_LEVEL_STDERR => {},\n"
|
|
"}),\n"
|
|
"$iLocalIdx + 1\n"
|
|
");\n"
|
|
"\n"
|
|
"my $hLocal =\n"
|
|
"{\n"
|
|
"iHostIdx => $iHostIdx,\n"
|
|
"iProcessId => $iProcessId,\n"
|
|
"iHostProcessIdx => $iHostProcessIdx,\n"
|
|
"oLocal => $oLocal,\n"
|
|
"hndIn => fileno($oLocal->io()->handleRead()),\n"
|
|
"};\n"
|
|
"\n"
|
|
"push(@{$self->{hyLocal}}, $hLocal);\n"
|
|
"\n"
|
|
"$self->{hLocalMap}{$hLocal->{hndIn}} = $hLocal;\n"
|
|
"$self->{oSelect}->add($hLocal->{hndIn});\n"
|
|
"}\n"
|
|
"\n"
|
|
"$iHostIdx++;\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'bResult', value => $iHostIdx > 0 ? true : false}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n\n\n"
|
|
"sub init\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my ($strOperation) = logDebugParam(__PACKAGE__ . '->init');\n"
|
|
"\n"
|
|
"if ($self->hostConnect())\n"
|
|
"{\n"
|
|
"foreach my $hLocal (@{$self->{hyLocal}})\n"
|
|
"{\n"
|
|
"my $hHost = $self->{hyHost}[$hLocal->{iHostIdx}];\n"
|
|
"my $hyQueue = $hHost->{hyQueue};\n"
|
|
"\n\n"
|
|
"$hLocal->{iDirection} = $hLocal->{iHostProcessIdx} % 2 == 0 ? 1 : -1;\n"
|
|
"$hLocal->{iQueueIdx} = int((@{$hyQueue} / $hHost->{iProcessMax}) * $hLocal->{iHostProcessIdx});\n"
|
|
"\n\n"
|
|
"$hLocal->{iQueueLastIdx} = $hLocal->{iQueueIdx} + ($hLocal->{iDirection} * -1);\n"
|
|
"\n"
|
|
"if ($hLocal->{iQueueLastIdx} < 0)\n"
|
|
"{\n"
|
|
"$hLocal->{iQueueLastIdx} = @{$hyQueue} - 1;\n"
|
|
"}\n"
|
|
"elsif ($hLocal->{iQueueLastIdx} >= @{$hyQueue})\n"
|
|
"{\n"
|
|
"$hLocal->{iQueueLastIdx} = 0;\n"
|
|
"}\n"
|
|
"\n"
|
|
"logDebugMisc(\n"
|
|
"$strOperation, 'init local process',\n"
|
|
"{name => 'iHostIdx', value => $hLocal->{iHostIdx}},\n"
|
|
"{name => 'iProcessId', value => $hLocal->{iProcessId}},\n"
|
|
"{name => 'iDirection', value => $hLocal->{iDirection}},\n"
|
|
"{name => 'iQueueIdx', value => $hLocal->{iQueueIdx}},\n"
|
|
"{name => 'iQueueLastIdx', value => $hLocal->{iQueueLastIdx}});\n"
|
|
"}\n"
|
|
"\n"
|
|
"$self->{bProcessing} = true;\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'bResult', value => $self->processing()}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n\n\n"
|
|
"sub process\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my ($strOperation) = logDebugParam(__PACKAGE__ . '->process');\n"
|
|
"\n\n"
|
|
"if (!$self->processing())\n"
|
|
"{\n"
|
|
"if (!$self->init())\n"
|
|
"{\n"
|
|
"logDebugMisc($strOperation, 'no jobs to run');\n"
|
|
"$self->reset();\n"
|
|
"return;\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"my @hyResult = ();\n"
|
|
"my $iCompleted = 0;\n"
|
|
"\n"
|
|
"if ($self->{iRunning} > 0)\n"
|
|
"{\n"
|
|
"&logDebugMisc(\n"
|
|
"$strOperation, 'check running jobs',\n"
|
|
"{name => 'iRunning', value => $self->{iRunning}, trace => true});\n"
|
|
"\n\n"
|
|
"my @hndyIn = $self->{oSelect}->can_read($self->{iSelectTimeout});\n"
|
|
"\n\n"
|
|
"foreach my $hndIn (@hndyIn)\n"
|
|
"{\n"
|
|
"\n"
|
|
"my $hLocal = $self->{hLocalMap}{$hndIn};\n"
|
|
"\n"
|
|
"if (!defined($hLocal))\n"
|
|
"{\n"
|
|
"confess &log(ASSERT, \"unable to map from fileno ${hndIn} to local\");\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"my $hJob = $hLocal->{hJob};\n"
|
|
"\n"
|
|
"eval\n"
|
|
"{\n"
|
|
"$hJob->{rResult} = $hLocal->{oLocal}->outputRead(true, undef, undef, true);\n"
|
|
"\n\n\n"
|
|
"if (ref($hJob->{rResult}) ne 'ARRAY')\n"
|
|
"{\n"
|
|
"my @resultArray = (${$hJob->{rResult}});\n"
|
|
"$hJob->{rResult} = \\@resultArray;\n"
|
|
"}\n"
|
|
"\n"
|
|
"return true;\n"
|
|
"}\n"
|
|
"or do\n"
|
|
"{\n"
|
|
"my $oException = $EVAL_ERROR;\n"
|
|
"\n\n"
|
|
"confess $oException if (!isException(\\$oException));\n"
|
|
"\n\n"
|
|
"if (!defined($hLocal->{oLocal}->io()->processId()))\n"
|
|
"{\n"
|
|
"confess logException($oException);\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if ($self->{bConfessError})\n"
|
|
"{\n"
|
|
"confess logException($oException);\n"
|
|
"}\n"
|
|
"\n"
|
|
"else\n"
|
|
"{\n"
|
|
"$hJob->{oException} = $oException;\n"
|
|
"}\n"
|
|
"};\n"
|
|
"\n"
|
|
"$hJob->{iProcessId} = $hLocal->{iProcessId};\n"
|
|
"push(@hyResult, $hJob);\n"
|
|
"\n"
|
|
"logDebugMisc(\n"
|
|
"$strOperation, 'job complete',\n"
|
|
"{name => 'iProcessId', value => $hJob->{iProcessId}},\n"
|
|
"{name => 'strKey', value => $hJob->{strKey}},\n"
|
|
"{name => 'rResult', value => $hJob->{rResult}});\n"
|
|
"\n\n"
|
|
"$hLocal->{hJob} = undef;\n"
|
|
"$self->{iRunning}--;\n"
|
|
"$iCompleted++;\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if ($self->{iRunning} == 0 || $iCompleted > 0)\n"
|
|
"{\n"
|
|
"&logDebugMisc(\n"
|
|
"$strOperation, 'get new jobs',\n"
|
|
"{name => 'iRunning', value => $self->{iRunning}, trace => true},\n"
|
|
"{name => 'iCompleted', value => $iCompleted, trace => true});\n"
|
|
"\n"
|
|
"my $bFound = false;\n"
|
|
"my $iLocalIdx = -1;\n"
|
|
"\n\n"
|
|
"foreach my $hLocal (@{$self->{hyLocal}})\n"
|
|
"{\n"
|
|
"\n"
|
|
"$iLocalIdx++;\n"
|
|
"next if (!defined($hLocal));\n"
|
|
"\n"
|
|
"my $hHost = $self->{hyHost}[$hLocal->{iHostIdx}];\n"
|
|
"my $hyQueue = $hHost->{hyQueue};\n"
|
|
"\n\n"
|
|
"if (!defined($hLocal->{hJob}))\n"
|
|
"{\n"
|
|
"\n"
|
|
"my $iQueueIdx = $hLocal->{iQueueIdx};\n"
|
|
"my $hJob = shift(@{$$hyQueue[$iQueueIdx]});\n"
|
|
"\n"
|
|
"while (!defined($hJob) && $iQueueIdx != $hLocal->{iQueueLastIdx})\n"
|
|
"{\n"
|
|
"$iQueueIdx += $hLocal->{iDirection};\n"
|
|
"\n"
|
|
"if ($iQueueIdx < 0)\n"
|
|
"{\n"
|
|
"$iQueueIdx = @{$hyQueue} - 1;\n"
|
|
"}\n"
|
|
"elsif ($iQueueIdx >= @{$hyQueue})\n"
|
|
"{\n"
|
|
"$iQueueIdx = 0;\n"
|
|
"}\n"
|
|
"\n"
|
|
"$hJob = shift(@{$$hyQueue[$iQueueIdx]});\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if (!defined($hJob))\n"
|
|
"{\n"
|
|
"logDebugMisc(\n"
|
|
"$strOperation, 'no jobs found, stop local',\n"
|
|
"{name => 'strHostType', value => $hLocal->{strHostType}},\n"
|
|
"{name => 'iHostConfigIdx', value => $hLocal->{iHostConfigIdx}},\n"
|
|
"{name => 'iHostIdx', value => $hLocal->{iHostIdx}},\n"
|
|
"{name => 'iProcessId', value => $hLocal->{iProcessId}});\n"
|
|
"\n\n"
|
|
"my $iHandleTotal = $self->{oSelect}->count();\n"
|
|
"\n"
|
|
"$self->{oSelect}->remove($hLocal->{hndIn});\n"
|
|
"\n"
|
|
"if ($iHandleTotal - $self->{oSelect}->count() != 1)\n"
|
|
"{\n"
|
|
"confess &log(ASSERT,\n"
|
|
"\"iProcessId $hLocal->{iProcessId}, handle $hLocal->{hndIn} was not removed from select object\");\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"delete($self->{hLocalMap}{$hLocal->{hndIn}});\n"
|
|
"\n\n"
|
|
"$hLocal->{oLocal}->close(true);\n"
|
|
"\n\n"
|
|
"undef(${$self->{hyLocal}}[$iLocalIdx]);\n"
|
|
"\n\n"
|
|
"next;\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"$hLocal->{hJob} = $hJob;\n"
|
|
"$bFound = true;\n"
|
|
"$self->{iRunning}++;\n"
|
|
"$self->{iQueued}--;\n"
|
|
"\n"
|
|
"logDebugMisc(\n"
|
|
"$strOperation, 'get job from queue',\n"
|
|
"{name => 'iHostIdx', value => $hLocal->{iHostIdx}},\n"
|
|
"{name => 'iProcessId', value => $hLocal->{iProcessId}},\n"
|
|
"{name => 'strQueueIdx', value => $iQueueIdx},\n"
|
|
"{name => 'strKey', value => $hLocal->{hJob}{strKey}});\n"
|
|
"\n\n"
|
|
"$hLocal->{oLocal}->cmdWrite($hLocal->{hJob}{strOp}, $hLocal->{hJob}->{rParam});\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if (!$bFound && !$self->{iRunning} && @hyResult == 0)\n"
|
|
"{\n"
|
|
"logDebugMisc($strOperation, 'all jobs complete');\n"
|
|
"$self->reset();\n"
|
|
"return;\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"return \\@hyResult;\n"
|
|
"}\n"
|
|
"\n\n\n\n\n\n"
|
|
"sub queueJob\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$iHostConfigIdx,\n"
|
|
"$strQueue,\n"
|
|
"$strKey,\n"
|
|
"$strOp,\n"
|
|
"$rParam,\n"
|
|
"$rParamSecure,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->queueJob', \\@_,\n"
|
|
"{name => 'iHostConfigIdx'},\n"
|
|
"{name => 'strQueue'},\n"
|
|
"{name => 'strKey'},\n"
|
|
"{name => 'strOp'},\n"
|
|
"{name => 'rParam'},\n"
|
|
"{name => 'rParamSecure', optional => true, redact => true},\n"
|
|
");\n"
|
|
"\n\n"
|
|
"if ($self->processing())\n"
|
|
"{\n"
|
|
"confess &log(ASSERT, 'new jobs cannot be added until processing is complete');\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if (defined($rParamSecure))\n"
|
|
"{\n"
|
|
"push(@{$rParam}, @{$rParamSecure});\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"my $hJob =\n"
|
|
"{\n"
|
|
"iHostConfigIdx => $iHostConfigIdx,\n"
|
|
"strQueue => $strQueue,\n"
|
|
"strKey => $strKey,\n"
|
|
"strOp => $strOp,\n"
|
|
"rParam => $rParam,\n"
|
|
"};\n"
|
|
"\n\n"
|
|
"my $iHostIdx = $self->{hHostMap}{$iHostConfigIdx};\n"
|
|
"\n"
|
|
"if (!defined($iHostIdx))\n"
|
|
"{\n"
|
|
"confess &log(ASSERT, \"iHostConfigIdx = $iHostConfigIdx does not exist\");\n"
|
|
"}\n"
|
|
"\n"
|
|
"my $hHost = $self->{hyHost}[$iHostIdx];\n"
|
|
"\n\n"
|
|
"my $iQueueIdx = $hHost->{hQueueMap}{$strQueue};\n"
|
|
"\n"
|
|
"if (!defined($iQueueIdx))\n"
|
|
"{\n"
|
|
"$iQueueIdx = defined($hHost->{hyQueue}) ? @{$hHost->{hyQueue}} : 0;\n"
|
|
"$hHost->{hQueueMap}{$strQueue} = $iQueueIdx;\n"
|
|
"}\n"
|
|
"\n"
|
|
"push(@{$hHost->{hyQueue}[$iQueueIdx]}, $hJob);\n"
|
|
"$self->{iQueued}++;\n"
|
|
"\n\n"
|
|
"return logDebugReturn($strOperation);\n"
|
|
"}\n"
|
|
"\n\n\n\n\n\n"
|
|
"sub dequeueJobs\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$iHostConfigIdx,\n"
|
|
"$strQueue,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->dequeueJobs', \\@_,\n"
|
|
"{name => 'iHostConfigIdx'},\n"
|
|
"{name => 'strQueue'},\n"
|
|
");\n"
|
|
"\n\n"
|
|
"if (!$self->processing())\n"
|
|
"{\n"
|
|
"confess &log(ASSERT, 'unable to dequeue a job when not processing');\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"my $iHostIdx = $self->{hHostMap}{$iHostConfigIdx};\n"
|
|
"\n"
|
|
"if (!defined($iHostIdx))\n"
|
|
"{\n"
|
|
"confess &log(ASSERT, \"iHostConfigIdx = $iHostConfigIdx does not exist\");\n"
|
|
"}\n"
|
|
"\n"
|
|
"my $hHost = $self->{hyHost}[$iHostIdx];\n"
|
|
"\n\n"
|
|
"my $iQueueIdx = $hHost->{hQueueMap}{$strQueue};\n"
|
|
"\n"
|
|
"if (!defined($iQueueIdx))\n"
|
|
"{\n"
|
|
"confess &log(ASSERT, \"unable to find queue '${strQueue}'\");\n"
|
|
"}\n"
|
|
"\n"
|
|
"$hHost->{hyQueue}[$iQueueIdx] = [];\n"
|
|
"$self->{iQueued} = 0;\n"
|
|
"\n\n"
|
|
"return logDebugReturn($strOperation);\n"
|
|
"}\n"
|
|
"\n\n\n\n\n\n"
|
|
"sub jobTotal\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my ($strOperation) = logDebugParam(__PACKAGE__ . '->jobTotal');\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'iJobTotal', value => $self->{iQueued} + $self->{iRunning}}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n\n\n"
|
|
"sub processing\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my ($strOperation) = logDebugParam(__PACKAGE__ . '->processing');\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'bProcessing', value => $self->{bProcessing}, trace => true}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n"
|
|
"1;\n"
|
|
},
|
|
{
|
|
.name = "pgBackRest/Protocol/Remote/Master.pm",
|
|
.data =
|
|
"\n\n\n"
|
|
"package pgBackRest::Protocol::Remote::Master;\n"
|
|
"use parent 'pgBackRest::Protocol::Command::Master';\n"
|
|
"\n"
|
|
"use strict;\n"
|
|
"use warnings FATAL => qw(all);\n"
|
|
"use Carp qw(confess);\n"
|
|
"\n"
|
|
"use File::Basename qw(dirname);\n"
|
|
"\n"
|
|
"use pgBackRest::Common::Log;\n"
|
|
"use pgBackRest::Config::Config;\n"
|
|
"use pgBackRest::Protocol::Command::Master;\n"
|
|
"\n\n\n\n"
|
|
"sub new\n"
|
|
"{\n"
|
|
"my $class = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$strCommandSSH,\n"
|
|
"$strCommand,\n"
|
|
"$iBufferMax,\n"
|
|
"$iCompressLevel,\n"
|
|
"$iCompressLevelNetwork,\n"
|
|
"$strHost,\n"
|
|
"$strUser,\n"
|
|
"$iSshPort,\n"
|
|
"$iProtocolTimeout,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->new', \\@_,\n"
|
|
"{name => 'strCommandSSH'},\n"
|
|
"{name => 'strCommand'},\n"
|
|
"{name => 'iBufferMax'},\n"
|
|
"{name => 'iCompressLevel'},\n"
|
|
"{name => 'iCompressLevelNetwork'},\n"
|
|
"{name => 'strHost'},\n"
|
|
"{name => 'strUser'},\n"
|
|
"{name => 'iSshPort', required => false},\n"
|
|
"{name => 'iProtocolTimeout'},\n"
|
|
");\n"
|
|
"\n"
|
|
"my $strCommandSshPort = defined($iSshPort) ? '-p ' . $iSshPort . ' ' : '';\n"
|
|
"\n\n"
|
|
"$strCommand =\n"
|
|
"\"${strCommandSSH} -o LogLevel=error -o Compression=no -o PasswordAuthentication=no $strCommandSshPort\" .\n"
|
|
"\"${strUser}\\@${strHost} '${strCommand}'\";\n"
|
|
"\n\n"
|
|
"my $self = $class->SUPER::new(\n"
|
|
"'remote', \"remote process on '$strHost'\", $strCommand, $iBufferMax, $iCompressLevel, $iCompressLevelNetwork,\n"
|
|
"$iProtocolTimeout);\n"
|
|
"bless $self, $class;\n"
|
|
"\n\n"
|
|
"$self->{strHost} = $strHost;\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'self', value => $self}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n"
|
|
"1;\n"
|
|
},
|
|
{
|
|
.name = "pgBackRest/Protocol/Remote/Minion.pm",
|
|
.data =
|
|
"\n\n\n"
|
|
"package pgBackRest::Protocol::Remote::Minion;\n"
|
|
"use parent 'pgBackRest::Protocol::Command::Minion';\n"
|
|
"\n"
|
|
"use strict;\n"
|
|
"use warnings FATAL => qw(all);\n"
|
|
"use Carp qw(confess);\n"
|
|
"\n"
|
|
"use File::Basename qw(dirname);\n"
|
|
"\n"
|
|
"use pgBackRest::Backup::File;\n"
|
|
"use pgBackRest::Common::Log;\n"
|
|
"use pgBackRest::Common::Io::Buffered;\n"
|
|
"use pgBackRest::Common::Wait;\n"
|
|
"use pgBackRest::Archive::Get::File;\n"
|
|
"use pgBackRest::Check::Check;\n"
|
|
"use pgBackRest::Config::Config;\n"
|
|
"use pgBackRest::Db;\n"
|
|
"use pgBackRest::Protocol::Command::Minion;\n"
|
|
"use pgBackRest::Protocol::Helper;\n"
|
|
"use pgBackRest::Protocol::Storage::Helper;\n"
|
|
"\n\n\n\n"
|
|
"sub new\n"
|
|
"{\n"
|
|
"my $class = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$iBufferMax,\n"
|
|
"$iProtocolTimeout\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->new', \\@_,\n"
|
|
"{name => 'iBufferMax'},\n"
|
|
"{name => 'iProtocolTimeout'}\n"
|
|
");\n"
|
|
"\n\n"
|
|
"my $self = $class->SUPER::new(cfgCommandName(CFGCMD_REMOTE), $iBufferMax, $iProtocolTimeout);\n"
|
|
"bless $self, $class;\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'self', value => $self}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub init\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my ($strOperation) = logDebugParam(__PACKAGE__ . '->init');\n"
|
|
"\n\n"
|
|
"my $oStorage = cfgOptionTest(CFGOPT_TYPE, CFGOPTVAL_REMOTE_TYPE_DB) ? storageDb() : storageRepo();\n"
|
|
"\n"
|
|
"my $oCheck = cfgOptionTest(CFGOPT_TYPE, CFGOPTVAL_REMOTE_TYPE_BACKUP) ? new pgBackRest::Check::Check() : undef;\n"
|
|
"my $oDb = cfgOptionTest(CFGOPT_TYPE, CFGOPTVAL_REMOTE_TYPE_DB) ? new pgBackRest::Db() : undef;\n"
|
|
"\n\n"
|
|
"my $hCommandMap =\n"
|
|
"{\n"
|
|
"\n"
|
|
"&OP_ARCHIVE_GET_CHECK => sub {archiveGetCheck(@{shift()})},\n"
|
|
"\n\n"
|
|
"&OP_CHECK_BACKUP_INFO_CHECK => sub {$oCheck->backupInfoCheck(@{shift()})},\n"
|
|
"\n\n"
|
|
"&OP_DB_CONNECT => sub {$oDb->connect()},\n"
|
|
"&OP_DB_EXECUTE_SQL => sub {$oDb->executeSql(@{shift()})},\n"
|
|
"&OP_DB_INFO => sub {$oDb->info(@{shift()})},\n"
|
|
"\n\n"
|
|
"&OP_STORAGE_OPEN_READ => sub\n"
|
|
"{\n"
|
|
"my $oSourceFileIo = $oStorage->openRead(@{shift()});\n"
|
|
"\n\n"
|
|
"if (defined($oSourceFileIo))\n"
|
|
"{\n"
|
|
"$self->outputWrite(true);\n"
|
|
"\n"
|
|
"$oStorage->copy($oSourceFileIo, new pgBackRest::Protocol::Storage::File($self, $oSourceFileIo));\n"
|
|
"\n"
|
|
"return true;\n"
|
|
"}\n"
|
|
"\n"
|
|
"return false;\n"
|
|
"},\n"
|
|
"&OP_STORAGE_OPEN_WRITE => sub\n"
|
|
"{\n"
|
|
"my $oDestinationFileIo = $oStorage->openWrite(@{shift()});\n"
|
|
"$oStorage->copy(new pgBackRest::Protocol::Storage::File($self, $oDestinationFileIo), $oDestinationFileIo);\n"
|
|
"},\n"
|
|
"\n"
|
|
"&OP_STORAGE_CIPHER_PASS_USER => sub {$oStorage->cipherPassUser()},\n"
|
|
"&OP_STORAGE_EXISTS => sub {$oStorage->exists(@{shift()})},\n"
|
|
"&OP_STORAGE_LIST => sub {$oStorage->list(@{shift()})},\n"
|
|
"&OP_STORAGE_MANIFEST => sub {$oStorage->manifest(@{shift()})},\n"
|
|
"&OP_STORAGE_MOVE => sub {$oStorage->move(@{shift()})},\n"
|
|
"&OP_STORAGE_PATH_GET => sub {$oStorage->pathGet(@{shift()})},\n"
|
|
"&OP_STORAGE_HASH_SIZE => sub {$oStorage->hashSize(@{shift()})},\n"
|
|
"\n\n"
|
|
"&OP_WAIT => sub {waitRemainder(@{shift()})},\n"
|
|
"};\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'hCommandMap', value => $hCommandMap}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n"
|
|
"1;\n"
|
|
},
|
|
{
|
|
.name = "pgBackRest/Protocol/Storage/File.pm",
|
|
.data =
|
|
"\n\n\n"
|
|
"package pgBackRest::Protocol::Storage::File;\n"
|
|
"use parent 'pgBackRest::Common::Io::Base';\n"
|
|
"\n"
|
|
"use strict;\n"
|
|
"use warnings FATAL => qw(all);\n"
|
|
"use Carp qw(confess);\n"
|
|
"use English '-no_match_vars';\n"
|
|
"\n"
|
|
"use Exporter qw(import);\n"
|
|
"our @EXPORT = qw();\n"
|
|
"\n"
|
|
"use pgBackRest::Common::Exception;\n"
|
|
"use pgBackRest::Common::Log;\n"
|
|
"\n\n\n\n"
|
|
"sub new\n"
|
|
"{\n"
|
|
"my $class = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$oProtocol,\n"
|
|
"$oFileIo,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->new', \\@_,\n"
|
|
"{name => 'oProtocol', trace => true},\n"
|
|
"{name => 'oFileIo', required => false, trace => true},\n"
|
|
");\n"
|
|
"\n\n"
|
|
"my $self = $class->SUPER::new($oProtocol->io()->id() . ' file');\n"
|
|
"bless $self, $class;\n"
|
|
"\n\n"
|
|
"$self->{oProtocol} = $oProtocol;\n"
|
|
"$self->{oFileIo} = $oFileIo;\n"
|
|
"\n\n"
|
|
"$self->{bWrite} = false;\n"
|
|
"\n\n"
|
|
"$self->eofSet(false);\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'self', value => $self}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub eof\n"
|
|
"{\n"
|
|
"return shift->{bEOF};\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub eofSet\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"my $bEOF = shift;\n"
|
|
"\n"
|
|
"$self->{bEOF} = $bEOF;\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub read\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"my $rtBuffer = shift;\n"
|
|
"\n\n"
|
|
"return 0 if $self->eof();\n"
|
|
"\n"
|
|
"my $lBlockSize;\n"
|
|
"\n\n"
|
|
"my $strBlockHeader = $self->{oProtocol}->io()->readLine();\n"
|
|
"\n"
|
|
"if ($strBlockHeader !~ /^BRBLOCK[0-9]+$/)\n"
|
|
"{\n"
|
|
"confess &log(ERROR, \"invalid block header '${strBlockHeader}'\", ERROR_FILE_READ);\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"$lBlockSize = substr($strBlockHeader, 7);\n"
|
|
"\n\n"
|
|
"if ($lBlockSize > 0)\n"
|
|
"{\n"
|
|
"$self->{oProtocol}->io()->read($rtBuffer, $lBlockSize, true);\n"
|
|
"}\n"
|
|
"else\n"
|
|
"{\n"
|
|
"$self->eofSet(true);\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"return $lBlockSize;\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub write\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"my $rtBuffer = shift;\n"
|
|
"\n\n"
|
|
"$self->{bWrite} = true;\n"
|
|
"\n\n"
|
|
"my $lBlockSize = defined($rtBuffer) ? length($$rtBuffer) : 0;\n"
|
|
"\n\n"
|
|
"if ($lBlockSize > 0)\n"
|
|
"{\n"
|
|
"\n"
|
|
"$self->{oProtocol}->io()->writeLine(\"BRBLOCK${lBlockSize}\");\n"
|
|
"\n\n"
|
|
"$self->{oProtocol}->io()->write($rtBuffer);\n"
|
|
"}\n"
|
|
"\n"
|
|
"return length($$rtBuffer);\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub close\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"if (defined($self->{oProtocol}))\n"
|
|
"{\n"
|
|
"\n"
|
|
"if ($self->{bWrite})\n"
|
|
"{\n"
|
|
"$self->{oProtocol}->io()->writeLine(\"BRBLOCK0\");\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if ($self->{oProtocol}->master())\n"
|
|
"{\n"
|
|
"($self->{rhResult}) = $self->{oProtocol}->outputRead();\n"
|
|
"\n\n"
|
|
"$self->{oProtocol}->outputRead();\n"
|
|
"}\n"
|
|
"\n"
|
|
"else\n"
|
|
"{\n"
|
|
"$self->{oProtocol}->outputWrite($self->{oFileIo}->resultAll());\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"delete($self->{oProtocol});\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n"
|
|
"1;\n"
|
|
},
|
|
{
|
|
.name = "pgBackRest/Protocol/Storage/Helper.pm",
|
|
.data =
|
|
"\n\n\n"
|
|
"package pgBackRest::Protocol::Storage::Helper;\n"
|
|
"\n"
|
|
"use strict;\n"
|
|
"use warnings FATAL => qw(all);\n"
|
|
"use Carp qw(confess);\n"
|
|
"\n"
|
|
"use Exporter qw(import);\n"
|
|
"our @EXPORT = qw();\n"
|
|
"use File::Basename qw(basename);\n"
|
|
"\n"
|
|
"use pgBackRest::Common::Log;\n"
|
|
"use pgBackRest::Config::Config;\n"
|
|
"use pgBackRest::Protocol::Helper;\n"
|
|
"use pgBackRest::Protocol::Storage::Remote;\n"
|
|
"use pgBackRest::Storage::Base;\n"
|
|
"use pgBackRest::Storage::Helper;\n"
|
|
"use pgBackRest::Storage::Local;\n"
|
|
"\n\n\n\n"
|
|
"use constant STORAGE_DB => '<DB>';\n"
|
|
"push @EXPORT, qw(STORAGE_DB);\n"
|
|
"\n"
|
|
"use constant STORAGE_REPO => '<REPO>';\n"
|
|
"push @EXPORT, qw(STORAGE_REPO);\n"
|
|
"use constant STORAGE_REPO_ARCHIVE => '<REPO:ARCHIVE>';\n"
|
|
"push @EXPORT, qw(STORAGE_REPO_ARCHIVE);\n"
|
|
"use constant STORAGE_REPO_BACKUP => '<REPO:BACKUP>';\n"
|
|
"push @EXPORT, qw(STORAGE_REPO_BACKUP);\n"
|
|
"\n\n\n\n"
|
|
"my $hStorage;\n"
|
|
"\n\n\n\n"
|
|
"sub storageDb\n"
|
|
"{\n"
|
|
"\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$iRemoteIdx,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '::storageDb', \\@_,\n"
|
|
"{name => 'iRemoteIdx', optional => true, default => cfgOptionValid(CFGOPT_HOST_ID) ? cfgOption(CFGOPT_HOST_ID) : 1,\n"
|
|
"trace => true},\n"
|
|
");\n"
|
|
"\n\n"
|
|
"if (!defined($hStorage->{&STORAGE_DB}{$iRemoteIdx}))\n"
|
|
"{\n"
|
|
"if (isDbLocal({iRemoteIdx => $iRemoteIdx}))\n"
|
|
"{\n"
|
|
"$hStorage->{&STORAGE_DB}{$iRemoteIdx} = new pgBackRest::Storage::Local(\n"
|
|
"cfgOption(cfgOptionIdFromIndex(CFGOPT_PG_PATH, $iRemoteIdx)), new pgBackRest::Storage::Posix::Driver(),\n"
|
|
"{strTempExtension => STORAGE_TEMP_EXT, lBufferMax => cfgOption(CFGOPT_BUFFER_SIZE)});\n"
|
|
"}\n"
|
|
"else\n"
|
|
"{\n"
|
|
"$hStorage->{&STORAGE_DB}{$iRemoteIdx} = new pgBackRest::Protocol::Storage::Remote(\n"
|
|
"protocolGet(CFGOPTVAL_REMOTE_TYPE_DB, $iRemoteIdx));\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'oStorageDb', value => $hStorage->{&STORAGE_DB}{$iRemoteIdx}, trace => true},\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n"
|
|
"push @EXPORT, qw(storageDb);\n"
|
|
"\n\n\n\n"
|
|
"sub storageRepoRule\n"
|
|
"{\n"
|
|
"my $strRule = shift;\n"
|
|
"my $strFile = shift;\n"
|
|
"my $strStanza = shift;\n"
|
|
"\n\n"
|
|
"my $strResultFile;\n"
|
|
"\n\n"
|
|
"if ($strRule eq STORAGE_REPO_ARCHIVE)\n"
|
|
"{\n"
|
|
"$strResultFile = \"archive\" . (defined($strStanza) ? \"/${strStanza}\" : '');\n"
|
|
"\n\n"
|
|
"if (defined($strFile))\n"
|
|
"{\n"
|
|
"my ($strArchiveId, $strWalFile) = split('/', $strFile);\n"
|
|
"\n\n"
|
|
"if (defined($strWalFile) && $strWalFile =~ /^[0-F]{24}/)\n"
|
|
"{\n"
|
|
"$strResultFile .= \"/${strArchiveId}/\" . substr($strWalFile, 0, 16) . \"/${strWalFile}\";\n"
|
|
"}\n"
|
|
"\n"
|
|
"else\n"
|
|
"{\n"
|
|
"$strResultFile .= \"/${strFile}\";\n"
|
|
"}\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n"
|
|
"elsif ($strRule eq STORAGE_REPO_BACKUP)\n"
|
|
"{\n"
|
|
"$strResultFile = \"backup\" . (defined($strStanza) ? \"/${strStanza}\" : '') . (defined($strFile) ? \"/${strFile}\" : '');\n"
|
|
"}\n"
|
|
"\n"
|
|
"else\n"
|
|
"{\n"
|
|
"confess &log(ASSERT, \"invalid \" . STORAGE_REPO . \" storage rule ${strRule}\");\n"
|
|
"}\n"
|
|
"\n"
|
|
"return $strResultFile;\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub storageRepo\n"
|
|
"{\n"
|
|
"\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$strStanza,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '::storageRepo', \\@_,\n"
|
|
"{name => 'strStanza', optional => true, trace => true},\n"
|
|
");\n"
|
|
"\n"
|
|
"if (!defined($strStanza))\n"
|
|
"{\n"
|
|
"if (cfgOptionValid(CFGOPT_STANZA) && cfgOptionTest(CFGOPT_STANZA))\n"
|
|
"{\n"
|
|
"$strStanza = cfgOption(CFGOPT_STANZA);\n"
|
|
"}\n"
|
|
"else\n"
|
|
"{\n"
|
|
"$strStanza = STORAGE_REPO;\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if (!defined($hStorage->{&STORAGE_REPO}{$strStanza}))\n"
|
|
"{\n"
|
|
"if (isRepoLocal())\n"
|
|
"{\n"
|
|
"\n"
|
|
"my $hRule =\n"
|
|
"{\n"
|
|
"&STORAGE_REPO_ARCHIVE =>\n"
|
|
"{\n"
|
|
"fnRule => \\&storageRepoRule,\n"
|
|
"xData => $strStanza eq STORAGE_REPO ? undef : $strStanza,\n"
|
|
"},\n"
|
|
"&STORAGE_REPO_BACKUP =>\n"
|
|
"{\n"
|
|
"fnRule => \\&storageRepoRule,\n"
|
|
"xData => $strStanza eq STORAGE_REPO ? undef : $strStanza,\n"
|
|
"},\n"
|
|
"};\n"
|
|
"\n\n"
|
|
"my $oDriver;\n"
|
|
"\n"
|
|
"if (cfgOptionTest(CFGOPT_REPO_TYPE, CFGOPTVAL_REPO_TYPE_S3))\n"
|
|
"{\n"
|
|
"require pgBackRest::Storage::S3::Driver;\n"
|
|
"\n"
|
|
"$oDriver = new pgBackRest::Storage::S3::Driver(\n"
|
|
"cfgOption(CFGOPT_REPO_S3_BUCKET), cfgOption(CFGOPT_REPO_S3_ENDPOINT), cfgOption(CFGOPT_REPO_S3_REGION),\n"
|
|
"cfgOption(CFGOPT_REPO_S3_KEY), cfgOption(CFGOPT_REPO_S3_KEY_SECRET),\n"
|
|
"{strHost => cfgOption(CFGOPT_REPO_S3_HOST, false), bVerifySsl => cfgOption(CFGOPT_REPO_S3_VERIFY_TLS, false),\n"
|
|
"strCaPath => cfgOption(CFGOPT_REPO_S3_CA_PATH, false),\n"
|
|
"strCaFile => cfgOption(CFGOPT_REPO_S3_CA_FILE, false), lBufferMax => cfgOption(CFGOPT_BUFFER_SIZE),\n"
|
|
"strSecurityToken => cfgOption(CFGOPT_REPO_S3_TOKEN, false)});\n"
|
|
"}\n"
|
|
"elsif (cfgOptionTest(CFGOPT_REPO_TYPE, CFGOPTVAL_REPO_TYPE_CIFS))\n"
|
|
"{\n"
|
|
"require pgBackRest::Storage::Cifs::Driver;\n"
|
|
"\n"
|
|
"$oDriver = new pgBackRest::Storage::Cifs::Driver();\n"
|
|
"}\n"
|
|
"else\n"
|
|
"{\n"
|
|
"$oDriver = new pgBackRest::Storage::Posix::Driver();\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"my $strCipherType;\n"
|
|
"my $strCipherPass;\n"
|
|
"\n\n"
|
|
"if (cfgOption(CFGOPT_REPO_CIPHER_TYPE) ne CFGOPTVAL_REPO_CIPHER_TYPE_NONE)\n"
|
|
"{\n"
|
|
"$strCipherType = cfgOption(CFGOPT_REPO_CIPHER_TYPE);\n"
|
|
"$strCipherPass = cfgOption(CFGOPT_REPO_CIPHER_PASS);\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"$hStorage->{&STORAGE_REPO}{$strStanza} = new pgBackRest::Storage::Local(\n"
|
|
"cfgOption(CFGOPT_REPO_PATH), $oDriver,\n"
|
|
"{strTempExtension => STORAGE_TEMP_EXT, hRule => $hRule, lBufferMax => cfgOption(CFGOPT_BUFFER_SIZE),\n"
|
|
"strCipherType => $strCipherType, strCipherPassUser => $strCipherPass});\n"
|
|
"}\n"
|
|
"else\n"
|
|
"{\n"
|
|
"\n"
|
|
"$hStorage->{&STORAGE_REPO}{$strStanza} = new pgBackRest::Protocol::Storage::Remote(\n"
|
|
"protocolGet(CFGOPTVAL_REMOTE_TYPE_BACKUP));\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'oStorageRepo', value => $hStorage->{&STORAGE_REPO}{$strStanza}, trace => true},\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n"
|
|
"push @EXPORT, qw(storageRepo);\n"
|
|
"\n\n\n\n"
|
|
"sub storageRepoCacheClear\n"
|
|
"{\n"
|
|
"\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$strStanza,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '::storageRepoCacheClear', \\@_,\n"
|
|
"{name => 'strStanza'},\n"
|
|
");\n"
|
|
"\n"
|
|
"if (defined($hStorage->{&STORAGE_REPO}{$strStanza}))\n"
|
|
"{\n"
|
|
"delete($hStorage->{&STORAGE_REPO}{$strStanza});\n"
|
|
"}\n"
|
|
"\n"
|
|
"return;\n"
|
|
"}\n"
|
|
"\n"
|
|
"push @EXPORT, qw(storageRepoCacheClear);\n"
|
|
"\n"
|
|
"1;\n"
|
|
},
|
|
{
|
|
.name = "pgBackRest/Protocol/Storage/Remote.pm",
|
|
.data =
|
|
"\n\n\n"
|
|
"package pgBackRest::Protocol::Storage::Remote;\n"
|
|
"use parent 'pgBackRest::Storage::Base';\n"
|
|
"\n"
|
|
"use strict;\n"
|
|
"use warnings FATAL => qw(all);\n"
|
|
"use Carp qw(confess);\n"
|
|
"use English '-no_match_vars';\n"
|
|
"\n"
|
|
"use pgBackRest::Common::Exception;\n"
|
|
"use pgBackRest::Common::Log;\n"
|
|
"use pgBackRest::Config::Config;\n"
|
|
"use pgBackRest::Protocol::Helper;\n"
|
|
"use pgBackRest::Protocol::Storage::File;\n"
|
|
"use pgBackRest::Storage::Base;\n"
|
|
"use pgBackRest::Storage::Filter::Gzip;\n"
|
|
"\n\n\n\n"
|
|
"sub new\n"
|
|
"{\n"
|
|
"my $class = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$oProtocol,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->new', \\@_,\n"
|
|
"{name => 'oProtocol'},\n"
|
|
");\n"
|
|
"\n\n"
|
|
"my $self = $class->SUPER::new({lBufferMax => $oProtocol->io()->bufferMax()});\n"
|
|
"bless $self, $class;\n"
|
|
"\n\n"
|
|
"$self->{oProtocol} = $oProtocol;\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'self', value => $self}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub exists\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$strPathExp,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->exists', \\@_,\n"
|
|
"{name => 'strPathExp'},\n"
|
|
");\n"
|
|
"\n"
|
|
"my $bExists = $self->{oProtocol}->cmdExecute(OP_STORAGE_EXISTS, [$strPathExp]);\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'bExists', value => $bExists}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub hashSize\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$strPathExp,\n"
|
|
"$rhParam,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->hashSize', \\@_,\n"
|
|
"{name => 'strPathExp'},\n"
|
|
"{name => 'rhParam', required => false},\n"
|
|
");\n"
|
|
"\n"
|
|
"my ($strHash, $lSize) = $self->{oProtocol}->cmdExecute(OP_STORAGE_HASH_SIZE, [$strPathExp, $rhParam]);\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'strHash', value => $strHash},\n"
|
|
"{name => 'lSize', value => $lSize}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub list\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$strPathExp,\n"
|
|
"$rhParam,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->list', \\@_,\n"
|
|
"{name => 'strPathExp'},\n"
|
|
"{name => 'rhParam', required => false},\n"
|
|
");\n"
|
|
"\n"
|
|
"my @stryFileList = $self->{oProtocol}->cmdExecute(OP_STORAGE_LIST, [$strPathExp, $rhParam]);\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'stryFileList', value => \\@stryFileList}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub manifest\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$strPathExp,\n"
|
|
"$rhParam,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->manifest', \\@_,\n"
|
|
"{name => 'strPathExp'},\n"
|
|
"{name => 'rhParam', required => false},\n"
|
|
");\n"
|
|
"\n"
|
|
"my $hManifest = $self->{oProtocol}->cmdExecute(OP_STORAGE_MANIFEST, [$strPathExp, $rhParam]);\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'hManifest', value => $hManifest, trace => true}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub openRead\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$strFileExp,\n"
|
|
"$rhParam,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->openRead', \\@_,\n"
|
|
"{name => 'strFileExp'},\n"
|
|
"{name => 'rhParam', required => false},\n"
|
|
");\n"
|
|
"\n\n"
|
|
"my $bProtocolCompress = protocolCompress($rhParam);\n"
|
|
"\n\n"
|
|
"if ($bProtocolCompress)\n"
|
|
"{\n"
|
|
"push(\n"
|
|
"@{$rhParam->{rhyFilter}},\n"
|
|
"{strClass => STORAGE_FILTER_GZIP,\n"
|
|
"rxyParam => [{iLevel => cfgOption(CFGOPT_COMPRESS_LEVEL_NETWORK), bWantGzip => false}]});\n"
|
|
"}\n"
|
|
"\n"
|
|
"my $oSourceFileIo =\n"
|
|
"$self->{oProtocol}->cmdExecute(OP_STORAGE_OPEN_READ, [$strFileExp, $rhParam]) ?\n"
|
|
"new pgBackRest::Protocol::Storage::File($self->{oProtocol}) : undef;\n"
|
|
"\n\n"
|
|
"if ($bProtocolCompress)\n"
|
|
"{\n"
|
|
"$oSourceFileIo = new pgBackRest::Storage::Filter::Gzip(\n"
|
|
"$oSourceFileIo, {strCompressType => STORAGE_DECOMPRESS, bWantGzip => false});\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'oFileIo', value => $oSourceFileIo, trace => true},\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub openWrite\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$strFileExp,\n"
|
|
"$rhParam,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->openWrite', \\@_,\n"
|
|
"{name => 'strFileExp'},\n"
|
|
"{name => 'rhParam', required => false},\n"
|
|
");\n"
|
|
"\n\n"
|
|
"my $bProtocolCompress = protocolCompress($rhParam);\n"
|
|
"\n\n"
|
|
"if ($bProtocolCompress)\n"
|
|
"{\n"
|
|
"push(\n"
|
|
"@{$rhParam->{rhyFilter}},\n"
|
|
"{strClass => STORAGE_FILTER_GZIP, rxyParam => [{strCompressType => STORAGE_DECOMPRESS, bWantGzip => false}]});\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"$self->{oProtocol}->cmdWrite(OP_STORAGE_OPEN_WRITE, [$strFileExp, $rhParam]);\n"
|
|
"my $oDestinationFileIo = new pgBackRest::Protocol::Storage::File($self->{oProtocol});\n"
|
|
"\n\n"
|
|
"if ($bProtocolCompress)\n"
|
|
"{\n"
|
|
"$oDestinationFileIo = new pgBackRest::Storage::Filter::Gzip(\n"
|
|
"$oDestinationFileIo, {iLevel => cfgOption(CFGOPT_COMPRESS_LEVEL_NETWORK), bWantGzip => false});\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'oFileIo', value => $oDestinationFileIo, trace => true},\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub pathGet\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$strPathExp,\n"
|
|
"$rhParam,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->pathGet', \\@_,\n"
|
|
"{name => 'strPathExp', required => false},\n"
|
|
"{name => 'rhParam', required => false},\n"
|
|
");\n"
|
|
"\n"
|
|
"my $strPath = $self->{oProtocol}->cmdExecute(OP_STORAGE_PATH_GET, [$strPathExp, $rhParam]);\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'strPath', value => $strPath}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub move\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$strSourcePathExp,\n"
|
|
"$strDestinationPathExp,\n"
|
|
"$rhParam,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->move', \\@_,\n"
|
|
"{name => 'strSourcePathExp'},\n"
|
|
"{name => 'strDestinationPathExp'},\n"
|
|
"{name => 'rhParam', required => false},\n"
|
|
");\n"
|
|
"\n"
|
|
"$self->{oProtocol}->cmdExecute(OP_STORAGE_MOVE, [$strSourcePathExp, $strDestinationPathExp, $rhParam], false);\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub cipherPassUser\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->cipherPassUser', \\@_,\n"
|
|
");\n"
|
|
"\n"
|
|
"my $strCipherPassUser = $self->{oProtocol}->cmdExecute(OP_STORAGE_CIPHER_PASS_USER);\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'strCipherPassUser', value => $strCipherPassUser, redact => true}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub protocolCompress\n"
|
|
"{\n"
|
|
"my $rhParam = shift;\n"
|
|
"\n"
|
|
"my $bProtocolCompress = false;\n"
|
|
"\n"
|
|
"if (defined($rhParam->{bProtocolCompress}))\n"
|
|
"{\n"
|
|
"$bProtocolCompress = $rhParam->{bProtocolCompress} && cfgOption(CFGOPT_COMPRESS_LEVEL_NETWORK) > 0 ? true : false;\n"
|
|
"delete($rhParam->{bProtocolCompress});\n"
|
|
"}\n"
|
|
"\n"
|
|
"return $bProtocolCompress;\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub protocol {shift->{oProtocol}};\n"
|
|
"\n"
|
|
"1;\n"
|
|
},
|
|
{
|
|
.name = "pgBackRest/Restore.pm",
|
|
.data =
|
|
"\n\n\n"
|
|
"package pgBackRest::Restore;\n"
|
|
"\n"
|
|
"use strict;\n"
|
|
"use warnings FATAL => qw(all);\n"
|
|
"use Carp qw(confess);\n"
|
|
"\n"
|
|
"use Cwd qw(abs_path);\n"
|
|
"use File::Basename qw(basename dirname);\n"
|
|
"use File::stat qw(lstat);\n"
|
|
"\n"
|
|
"use pgBackRest::Backup::Info;\n"
|
|
"use pgBackRest::Common::Exception;\n"
|
|
"use pgBackRest::Common::Ini;\n"
|
|
"use pgBackRest::Common::Log;\n"
|
|
"use pgBackRest::Config::Config;\n"
|
|
"use pgBackRest::DbVersion;\n"
|
|
"use pgBackRest::Manifest;\n"
|
|
"use pgBackRest::RestoreFile;\n"
|
|
"use pgBackRest::Protocol::Helper;\n"
|
|
"use pgBackRest::Protocol::Local::Process;\n"
|
|
"use pgBackRest::Protocol::Storage::Helper;\n"
|
|
"use pgBackRest::Storage::Helper;\n"
|
|
"use pgBackRest::Version;\n"
|
|
"\n\n\n\n"
|
|
"sub new\n"
|
|
"{\n"
|
|
"my $class = shift;\n"
|
|
"\n\n"
|
|
"my $self = {};\n"
|
|
"bless $self, $class;\n"
|
|
"\n\n"
|
|
"my ($strOperation) = logDebugParam(__PACKAGE__ . '->new');\n"
|
|
"\n\n"
|
|
"$self->{oProtocol} = !isRepoLocal() ? protocolGet(CFGOPTVAL_REMOTE_TYPE_BACKUP) : undef;\n"
|
|
"\n\n"
|
|
"$self->{strDbClusterPath} = cfgOption(CFGOPT_PG_PATH);\n"
|
|
"$self->{strBackupSet} = cfgOption(CFGOPT_SET);\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'self', value => $self}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n\n\n\n"
|
|
"sub manifestOwnershipCheck\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$oManifest\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->manifestOwnershipCheck', \\@_,\n"
|
|
"{name => 'oManifest'}\n"
|
|
");\n"
|
|
"\n\n"
|
|
"my %oOwnerHash = ();\n"
|
|
"\n\n"
|
|
"my %oFileTypeHash =\n"
|
|
"(\n"
|
|
"&MANIFEST_SECTION_TARGET_PATH => true,\n"
|
|
"&MANIFEST_SECTION_TARGET_LINK => true,\n"
|
|
"&MANIFEST_SECTION_TARGET_FILE => true\n"
|
|
");\n"
|
|
"\n\n\n\n"
|
|
"my %oOwnerTypeHash;\n"
|
|
"\n"
|
|
"if ($oManifest->test(MANIFEST_SECTION_TARGET_PATH, MANIFEST_TARGET_PGDATA, MANIFEST_SUBKEY_USER) &&\n"
|
|
"!$oManifest->boolTest(MANIFEST_SECTION_TARGET_PATH, MANIFEST_TARGET_PGDATA, MANIFEST_SUBKEY_USER, false))\n"
|
|
"{\n"
|
|
"$oOwnerTypeHash{&MANIFEST_SUBKEY_USER} =\n"
|
|
"$oManifest->get(MANIFEST_SECTION_TARGET_PATH, MANIFEST_TARGET_PGDATA, MANIFEST_SUBKEY_USER);\n"
|
|
"}\n"
|
|
"else\n"
|
|
"{\n"
|
|
"$oOwnerTypeHash{&MANIFEST_SUBKEY_USER} = getpwuid($<);\n"
|
|
"\n"
|
|
"if (!defined($oOwnerTypeHash{&MANIFEST_SUBKEY_USER}))\n"
|
|
"{\n"
|
|
"confess &log(ERROR_USER_MISSING, 'current user uid does not map to a name');\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n"
|
|
"if ($oManifest->test(MANIFEST_SECTION_TARGET_PATH, MANIFEST_TARGET_PGDATA, MANIFEST_SUBKEY_GROUP) &&\n"
|
|
"!$oManifest->boolTest(MANIFEST_SECTION_TARGET_PATH, MANIFEST_TARGET_PGDATA, MANIFEST_SUBKEY_GROUP, false))\n"
|
|
"{\n"
|
|
"$oOwnerTypeHash{&MANIFEST_SUBKEY_GROUP} =\n"
|
|
"$oManifest->get(MANIFEST_SECTION_TARGET_PATH, MANIFEST_TARGET_PGDATA, MANIFEST_SUBKEY_GROUP);\n"
|
|
"}\n"
|
|
"else\n"
|
|
"{\n"
|
|
"$oOwnerTypeHash{&MANIFEST_SUBKEY_GROUP} = getgrgid($();\n"
|
|
"\n"
|
|
"if (!defined($oOwnerTypeHash{&MANIFEST_SUBKEY_GROUP}))\n"
|
|
"{\n"
|
|
"confess &log(ERROR_GROUP_MISSING, 'current user gid does not map to a name');\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"foreach my $strOwnerType (sort (keys %oOwnerTypeHash))\n"
|
|
"{\n"
|
|
"\n"
|
|
"foreach my $strSection (sort (keys %oFileTypeHash))\n"
|
|
"{\n"
|
|
"foreach my $strName ($oManifest->keys($strSection))\n"
|
|
"{\n"
|
|
"my $strOwner = $oManifest->get($strSection, $strName, $strOwnerType);\n"
|
|
"\n\n"
|
|
"if ($oManifest->boolTest($strSection, $strName, $strOwnerType, false))\n"
|
|
"{\n"
|
|
"$strOwner = $oOwnerTypeHash{$strOwnerType};\n"
|
|
"\n"
|
|
"&log(WARN, \"backup ${strOwnerType} for ${strName} was not mapped to a name, set to ${strOwner}\");\n"
|
|
"$oManifest->set($strSection, $strName, $strOwnerType, $strOwner);\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if ($< == 0)\n"
|
|
"{\n"
|
|
"\n"
|
|
"if (!defined($oOwnerHash{$strOwnerType}{$strOwner}))\n"
|
|
"{\n"
|
|
"my $strOwnerId;\n"
|
|
"\n"
|
|
"if ($strOwnerType eq 'user')\n"
|
|
"{\n"
|
|
"$strOwnerId = getpwnam($strOwner);\n"
|
|
"}\n"
|
|
"else\n"
|
|
"{\n"
|
|
"$strOwnerId = getgrnam($strOwner);\n"
|
|
"}\n"
|
|
"\n"
|
|
"$oOwnerHash{$strOwnerType}{$strOwner} = defined($strOwnerId) ? true : false;\n"
|
|
"}\n"
|
|
"\n"
|
|
"if (!$oOwnerHash{$strOwnerType}{$strOwner})\n"
|
|
"{\n"
|
|
"$oManifest->set($strSection, $strName, $strOwnerType, $oOwnerTypeHash{$strOwnerType});\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n"
|
|
"else\n"
|
|
"{\n"
|
|
"if ($strOwner ne $oOwnerTypeHash{$strOwnerType})\n"
|
|
"{\n"
|
|
"$oOwnerHash{$strOwnerType}{$strOwner} = false;\n"
|
|
"$oManifest->set($strSection, $strName, $strOwnerType, $oOwnerTypeHash{$strOwnerType});\n"
|
|
"}\n"
|
|
"}\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if (defined($oOwnerHash{$strOwnerType}))\n"
|
|
"{\n"
|
|
"foreach my $strOwner (sort (keys(%{$oOwnerHash{$strOwnerType}})))\n"
|
|
"{\n"
|
|
"if (!$oOwnerHash{$strOwnerType}{$strOwner})\n"
|
|
"{\n"
|
|
"&log(WARN, \"${strOwnerType} ${strOwner} in manifest \" . ($< == 0 ? 'does not exist locally ' : '') .\n"
|
|
"\"cannot be used for restore, set to $oOwnerTypeHash{$strOwnerType}\");\n"
|
|
"}\n"
|
|
"}\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"return logDebugReturn($strOperation);\n"
|
|
"}\n"
|
|
"\n\n\n\n\n\n"
|
|
"sub manifestLoad\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$strCipherPass,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->manifestLoad', \\@_,\n"
|
|
"{name => 'strCipherPass', required => false, redact => true},\n"
|
|
");\n"
|
|
"\n\n"
|
|
"if (!storageRepo()->exists(STORAGE_REPO_BACKUP . \"/$self->{strBackupSet}/\" . FILE_MANIFEST))\n"
|
|
"{\n"
|
|
"confess &log(ERROR, \"backup '$self->{strBackupSet}' does not exist\");\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"storageDb()->copy(\n"
|
|
"storageRepo()->openRead(STORAGE_REPO_BACKUP . \"/$self->{strBackupSet}/\" . FILE_MANIFEST, {bProtocolCompress => true,\n"
|
|
"strCipherPass => $strCipherPass}),\n"
|
|
"$self->{strDbClusterPath} . '/' . FILE_MANIFEST);\n"
|
|
"\n\n"
|
|
"my $oManifest = new pgBackRest::Manifest(\n"
|
|
"storageDb()->pathGet($self->{strDbClusterPath} . '/' . FILE_MANIFEST), {oStorage => storageDb()});\n"
|
|
"\n\n"
|
|
"my $strBackupLabel = $oManifest->get(MANIFEST_SECTION_BACKUP, MANIFEST_KEY_LABEL);\n"
|
|
"\n"
|
|
"if ($self->{strBackupSet} eq cfgOptionDefault(CFGOPT_SET))\n"
|
|
"{\n"
|
|
"$self->{strBackupSet} = $strBackupLabel;\n"
|
|
"}\n"
|
|
"elsif ($self->{strBackupSet} ne $strBackupLabel)\n"
|
|
"{\n"
|
|
"confess &log(ASSERT, \"request backup $self->{strBackupSet} and label ${strBackupLabel} do not match \" .\n"
|
|
"' - this indicates some sort of corruption (at the very least paths have been renamed)');\n"
|
|
"}\n"
|
|
"\n"
|
|
"if ($self->{strDbClusterPath} ne $oManifest->get(MANIFEST_SECTION_BACKUP_TARGET, MANIFEST_TARGET_PGDATA, MANIFEST_SUBKEY_PATH))\n"
|
|
"{\n"
|
|
"&log(INFO, 'remap $PGDATA directory to ' . $self->{strDbClusterPath});\n"
|
|
"\n"
|
|
"$oManifest->set(MANIFEST_SECTION_BACKUP_TARGET, MANIFEST_TARGET_PGDATA, MANIFEST_SUBKEY_PATH, $self->{strDbClusterPath});\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"my $oTablespaceRemap;\n"
|
|
"\n"
|
|
"if (cfgOptionTest(CFGOPT_TABLESPACE_MAP))\n"
|
|
"{\n"
|
|
"my $oTablespaceRemapRequest = cfgOption(CFGOPT_TABLESPACE_MAP);\n"
|
|
"\n"
|
|
"for my $strKey (sort(keys(%{$oTablespaceRemapRequest})))\n"
|
|
"{\n"
|
|
"my $bFound = false;\n"
|
|
"\n"
|
|
"for my $strTarget ($oManifest->keys(MANIFEST_SECTION_BACKUP_TARGET))\n"
|
|
"{\n"
|
|
"if ($oManifest->test(MANIFEST_SECTION_BACKUP_TARGET, $strTarget, MANIFEST_SUBKEY_TABLESPACE_ID, $strKey) ||\n"
|
|
"$oManifest->test(MANIFEST_SECTION_BACKUP_TARGET, $strTarget, MANIFEST_SUBKEY_TABLESPACE_NAME, $strKey))\n"
|
|
"{\n"
|
|
"if (defined(${$oTablespaceRemap}{$strTarget}))\n"
|
|
"{\n"
|
|
"confess &log(ERROR, \"tablespace ${strKey} has already been remapped to ${$oTablespaceRemap}{$strTarget}\",\n"
|
|
"ERROR_TABLESPACE_MAP);\n"
|
|
"}\n"
|
|
"\n"
|
|
"${$oTablespaceRemap}{$strTarget} = ${$oTablespaceRemapRequest}{$strKey};\n"
|
|
"$bFound = true;\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if (!$bFound)\n"
|
|
"{\n"
|
|
"confess &log(ERROR, \"cannot remap invalid tablespace ${strKey} to ${$oTablespaceRemapRequest}{$strKey}\",\n"
|
|
"ERROR_TABLESPACE_MAP);\n"
|
|
"}\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if (cfgOptionTest(CFGOPT_TABLESPACE_MAP_ALL))\n"
|
|
"{\n"
|
|
"for my $strTarget ($oManifest->keys(MANIFEST_SECTION_BACKUP_TARGET))\n"
|
|
"{\n"
|
|
"if ($oManifest->isTargetTablespace($strTarget))\n"
|
|
"{\n"
|
|
"if (!defined(${$oTablespaceRemap}{$strTarget}))\n"
|
|
"{\n"
|
|
"${$oTablespaceRemap}{$strTarget} = cfgOption(CFGOPT_TABLESPACE_MAP_ALL) . '/' .\n"
|
|
"$oManifest->get(MANIFEST_SECTION_BACKUP_TARGET, $strTarget, MANIFEST_SUBKEY_TABLESPACE_NAME);\n"
|
|
"}\n"
|
|
"}\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if ($oManifest->get(MANIFEST_SECTION_BACKUP_DB, MANIFEST_KEY_DB_VERSION) < PG_VERSION_92 &&\n"
|
|
"(cfgOptionTest(CFGOPT_TABLESPACE_MAP) || cfgOptionTest(CFGOPT_TABLESPACE_MAP_ALL)))\n"
|
|
"{\n"
|
|
"&log(WARN, \"update pg_tablespace.spclocation with new tablespace location in PostgreSQL < \" . PG_VERSION_92);\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if (defined($oTablespaceRemap))\n"
|
|
"{\n"
|
|
"foreach my $strTarget (sort(keys(%{$oTablespaceRemap})))\n"
|
|
"{\n"
|
|
"my $strRemapPath = ${$oTablespaceRemap}{$strTarget};\n"
|
|
"\n\n"
|
|
"&log(INFO, \"remap tablespace ${strTarget} directory to ${strRemapPath}\");\n"
|
|
"\n"
|
|
"$oManifest->set(MANIFEST_SECTION_BACKUP_TARGET, $strTarget, MANIFEST_SUBKEY_PATH, $strRemapPath);\n"
|
|
"$oManifest->set(MANIFEST_SECTION_TARGET_LINK, MANIFEST_TARGET_PGDATA . \"/${strTarget}\", MANIFEST_SUBKEY_DESTINATION,\n"
|
|
"$strRemapPath);\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n"
|
|
"$self->manifestOwnershipCheck($oManifest);\n"
|
|
"\n\n"
|
|
"my $oLinkRemap;\n"
|
|
"\n"
|
|
"if (cfgOptionTest(CFGOPT_LINK_MAP))\n"
|
|
"{\n"
|
|
"my $oLinkRemapRequest = cfgOption(CFGOPT_LINK_MAP);\n"
|
|
"\n"
|
|
"for my $strKey (sort(keys(%{$oLinkRemapRequest})))\n"
|
|
"{\n"
|
|
"my $strTarget = MANIFEST_TARGET_PGDATA . \"/${strKey}\";\n"
|
|
"\n\n"
|
|
"if ($oManifest->isTargetValid($strTarget, false) &&\n"
|
|
"$oManifest->isTargetLink($strTarget) && !$oManifest->isTargetTablespace($strTarget))\n"
|
|
"{\n"
|
|
"if (defined(${$oTablespaceRemap}{$strTarget}))\n"
|
|
"{\n"
|
|
"confess &log(ERROR, \"tablespace ${strKey} has already been remapped to ${$oLinkRemap}{$strTarget}\",\n"
|
|
"ERROR_LINK_MAP);\n"
|
|
"}\n"
|
|
"\n"
|
|
"${$oLinkRemap}{$strTarget} = ${$oLinkRemapRequest}{$strKey};\n"
|
|
"\n"
|
|
"&log(INFO, \"remap link ${strTarget} destination to ${$oLinkRemap}{$strTarget}\");\n"
|
|
"}\n"
|
|
"\n"
|
|
"else\n"
|
|
"{\n"
|
|
"confess &log(ERROR, \"cannot remap invalid link ${strKey} to ${$oLinkRemapRequest}{$strKey}\",\n"
|
|
"ERROR_LINK_MAP);\n"
|
|
"}\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if (cfgOption(CFGOPT_LINK_ALL))\n"
|
|
"{\n"
|
|
"for my $strTarget ($oManifest->keys(MANIFEST_SECTION_BACKUP_TARGET))\n"
|
|
"{\n"
|
|
"\n"
|
|
"if ($oManifest->isTargetLink($strTarget) && !$oManifest->isTargetTablespace($strTarget) &&\n"
|
|
"!defined(${$oLinkRemap}{$strTarget}))\n"
|
|
"{\n"
|
|
"${$oLinkRemap}{$strTarget} =\n"
|
|
"$oManifest->get(MANIFEST_SECTION_BACKUP_TARGET, $strTarget, MANIFEST_SUBKEY_PATH);\n"
|
|
"\n"
|
|
"if ($oManifest->isTargetFile($strTarget))\n"
|
|
"{\n"
|
|
"${$oLinkRemap}{$strTarget} .= '/' .\n"
|
|
"$oManifest->get(MANIFEST_SECTION_BACKUP_TARGET, $strTarget, MANIFEST_SUBKEY_FILE);\n"
|
|
"}\n"
|
|
"}\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"for my $strTarget ($oManifest->keys(MANIFEST_SECTION_BACKUP_TARGET))\n"
|
|
"{\n"
|
|
"if ($oManifest->isTargetLink($strTarget) && !$oManifest->isTargetTablespace($strTarget))\n"
|
|
"{\n"
|
|
"\n"
|
|
"if (defined(${$oLinkRemap}{$strTarget}))\n"
|
|
"{\n"
|
|
"my $strTargetPath = ${$oLinkRemap}{$strTarget};\n"
|
|
"\n\n"
|
|
"if ($oManifest->isTargetFile($strTarget))\n"
|
|
"{\n"
|
|
"$strTargetPath = dirname($strTargetPath);\n"
|
|
"\n\n"
|
|
"if (!defined($strTargetPath))\n"
|
|
"{\n"
|
|
"confess &log(ERROR, \"${$oLinkRemap}{$strTarget} is not long enough to be target for ${strTarget}\");\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"$oManifest->set(MANIFEST_SECTION_BACKUP_TARGET, $strTarget, MANIFEST_SUBKEY_FILE,\n"
|
|
"substr(${$oLinkRemap}{$strTarget}, length($strTargetPath) + 1));\n"
|
|
"\n\n"
|
|
"$oManifest->set(\n"
|
|
"MANIFEST_SECTION_TARGET_LINK, $strTarget, MANIFEST_SUBKEY_DESTINATION, ${$oLinkRemap}{$strTarget});\n"
|
|
"}\n"
|
|
"else\n"
|
|
"{\n"
|
|
"\n"
|
|
"$oManifest->set(MANIFEST_SECTION_TARGET_LINK, $strTarget, MANIFEST_SUBKEY_DESTINATION, $strTargetPath);\n"
|
|
"\n\n"
|
|
"$oManifest->remove(MANIFEST_SECTION_TARGET_PATH, $strTarget);\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"$oManifest->set(MANIFEST_SECTION_BACKUP_TARGET, $strTarget, MANIFEST_SUBKEY_PATH, $strTargetPath);\n"
|
|
"}\n"
|
|
"\n"
|
|
"else\n"
|
|
"{\n"
|
|
"if ($oManifest->test(MANIFEST_SECTION_BACKUP_TARGET, $strTarget, MANIFEST_SUBKEY_FILE))\n"
|
|
"{\n"
|
|
"&log(WARN, 'file link ' . $oManifest->dbPathGet(undef, $strTarget) .\n"
|
|
"' will be restored as a file at the same location');\n"
|
|
"}\n"
|
|
"else\n"
|
|
"{\n"
|
|
"&log(WARN, 'contents of directory link ' . $oManifest->dbPathGet(undef, $strTarget) .\n"
|
|
"' will be restored in a directory at the same location');\n"
|
|
"}\n"
|
|
"\n"
|
|
"$oManifest->remove(MANIFEST_SECTION_BACKUP_TARGET, $strTarget);\n"
|
|
"$oManifest->remove(MANIFEST_SECTION_TARGET_LINK, $strTarget);\n"
|
|
"}\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"$oManifest->linkCheck();\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'oManifest', value => $oManifest, trace => true}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n\n\n\n"
|
|
"sub clean\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$oManifest\n"
|
|
"\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->clean', \\@_,\n"
|
|
"{name => 'oManifest'}\n"
|
|
");\n"
|
|
"\n\n"
|
|
"my $oStorageDb = storageDb();\n"
|
|
"\n\n"
|
|
"my %oRemoveHash =\n"
|
|
"(\n"
|
|
"&MANIFEST_SECTION_TARGET_FILE => 0,\n"
|
|
"&MANIFEST_SECTION_TARGET_PATH => 0,\n"
|
|
"&MANIFEST_SECTION_TARGET_LINK => 0\n"
|
|
");\n"
|
|
"\n\n"
|
|
"my %oTargetFound;\n"
|
|
"my $bDelta = cfgOption(CFGOPT_FORCE) || cfgOption(CFGOPT_DELTA);\n"
|
|
"\n"
|
|
"for my $strTarget ($oManifest->keys(MANIFEST_SECTION_BACKUP_TARGET))\n"
|
|
"{\n"
|
|
"${$self->{oTargetPath}}{$strTarget} = $oManifest->get(MANIFEST_SECTION_BACKUP_TARGET, $strTarget, MANIFEST_SUBKEY_PATH);\n"
|
|
"$oTargetFound{$strTarget} = false;\n"
|
|
"\n"
|
|
"my $strCheckPath = ${$self->{oTargetPath}}{$strTarget};\n"
|
|
"\n"
|
|
"if ($oManifest->isTargetLink($strTarget) && index($strCheckPath, '/') != 0)\n"
|
|
"{\n"
|
|
"my $strBasePath = $oManifest->get(MANIFEST_SECTION_BACKUP_TARGET, MANIFEST_TARGET_PGDATA, MANIFEST_SUBKEY_PATH);\n"
|
|
"my $strTargetPath = dirname($oManifest->dbPathGet(undef, $strTarget));\n"
|
|
"\n"
|
|
"if ($strTargetPath ne '.')\n"
|
|
"{\n"
|
|
"$strBasePath .= \"/${strTargetPath}\";\n"
|
|
"}\n"
|
|
"\n"
|
|
"${$self->{oTargetPath}}{$strTarget} = $oStorageDb->pathAbsolute($strBasePath, $strCheckPath);\n"
|
|
"\n"
|
|
"$strCheckPath = ${$self->{oTargetPath}}{$strTarget};\n"
|
|
"\n"
|
|
"if ($oManifest->isTargetTablespace($strTarget))\n"
|
|
"{\n"
|
|
"$strCheckPath = dirname(${$self->{oTargetPath}}{$strTarget});\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n"
|
|
"&log(DETAIL, \"check ${strCheckPath} exists\");\n"
|
|
"\n\n"
|
|
"if (!$oStorageDb->pathExists($strCheckPath))\n"
|
|
"{\n"
|
|
"confess &log(ERROR, \"cannot restore to missing path ${strCheckPath}\", ERROR_PATH_MISSING);\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if ($oManifest->isTargetFile($strTarget))\n"
|
|
"{\n"
|
|
"\n"
|
|
"my $strCheckFile = \"${strCheckPath}/\" .\n"
|
|
"$oManifest->get(MANIFEST_SECTION_BACKUP_TARGET, $strTarget, MANIFEST_SUBKEY_FILE);\n"
|
|
"\n\n"
|
|
"if ($oStorageDb->exists($strCheckFile))\n"
|
|
"{\n"
|
|
"\n"
|
|
"if (!$bDelta)\n"
|
|
"{\n"
|
|
"confess &log(ERROR, \"cannot restore file '${strCheckFile}' that already exists - \" .\n"
|
|
"'try using --delta if this is what you intended', ERROR_PATH_NOT_EMPTY);\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"$oTargetFound{$strTarget} = true;\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n"
|
|
"else\n"
|
|
"{\n"
|
|
"\n"
|
|
"if ($oManifest->isTargetTablespace($strTarget))\n"
|
|
"{\n"
|
|
"\n"
|
|
"${$self->{oTargetPath}}{$strTarget} = \"${$self->{oTargetPath}}{$strTarget}\" .\n"
|
|
"(($oManifest->dbVersion() >= PG_VERSION_90) ? \"/\" . $oManifest->tablespacePathGet() : \"\");\n"
|
|
"\n\n"
|
|
"if (!$oStorageDb->pathExists(${$self->{oTargetPath}}{$strTarget}))\n"
|
|
"{\n"
|
|
"next;\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"my $hTargetManifest = $oStorageDb->manifest(${$self->{oTargetPath}}{$strTarget});\n"
|
|
"\n"
|
|
"for my $strName (keys(%{$hTargetManifest}))\n"
|
|
"{\n"
|
|
"\n"
|
|
"if ($strName eq '.' ||\n"
|
|
"(($strName eq FILE_MANIFEST || $strName eq DB_FILE_RECOVERYCONF) && $strTarget eq MANIFEST_TARGET_PGDATA))\n"
|
|
"{\n"
|
|
"next;\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if (!$bDelta)\n"
|
|
"{\n"
|
|
"confess &log(ERROR, \"cannot restore to path '${$self->{oTargetPath}}{$strTarget}' that contains files - \" .\n"
|
|
"'try using --delta if this is what you intended', ERROR_PATH_NOT_EMPTY);\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"$oTargetFound{$strTarget} = true;\n"
|
|
"}\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"my %oFileChecked;\n"
|
|
"\n"
|
|
"for my $strTarget ($oManifest->keys(MANIFEST_SECTION_BACKUP_TARGET, INI_SORT_REVERSE))\n"
|
|
"{\n"
|
|
"if ($oTargetFound{$strTarget})\n"
|
|
"{\n"
|
|
"&log(INFO, \"remove invalid files/paths/links from ${$self->{oTargetPath}}{$strTarget}\");\n"
|
|
"\n\n"
|
|
"if (!$oStorageDb->pathExists(${$self->{oTargetPath}}{$strTarget}) &&\n"
|
|
"$oManifest->isTargetTablespace($strTarget))\n"
|
|
"{\n"
|
|
"next;\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"my $hTargetManifest = $oStorageDb->manifest(${$self->{oTargetPath}}{$strTarget});\n"
|
|
"\n\n"
|
|
"if ($oManifest->isTargetFile($strTarget))\n"
|
|
"{\n"
|
|
"next;\n"
|
|
"}\n"
|
|
"\n"
|
|
"foreach my $strName (sort {$b cmp $a} (keys(%{$hTargetManifest})))\n"
|
|
"{\n"
|
|
"\n"
|
|
"if ($strName eq '.' || ($strName eq FILE_MANIFEST && $strTarget eq MANIFEST_TARGET_PGDATA))\n"
|
|
"{\n"
|
|
"next;\n"
|
|
"}\n"
|
|
"\n"
|
|
"my $strOsFile = \"${$self->{oTargetPath}}{$strTarget}/${strName}\";\n"
|
|
"my $strManifestFile = $oManifest->repoPathGet($strTarget, $strName);\n"
|
|
"\n\n"
|
|
"my $strSection = MANIFEST_SECTION_TARGET_FILE;\n"
|
|
"\n"
|
|
"if ($hTargetManifest->{$strName}{type} eq 'd')\n"
|
|
"{\n"
|
|
"$strSection = MANIFEST_SECTION_TARGET_PATH;\n"
|
|
"}\n"
|
|
"elsif ($hTargetManifest->{$strName}{type} eq 'l')\n"
|
|
"{\n"
|
|
"$strSection = MANIFEST_SECTION_TARGET_LINK;\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if ($oManifest->test($strSection, $strManifestFile) &&\n"
|
|
"!defined($oFileChecked{$strSection}{$strManifestFile}))\n"
|
|
"{\n"
|
|
"my $strUser = $oManifest->get($strSection, $strManifestFile, MANIFEST_SUBKEY_USER);\n"
|
|
"my $strGroup = $oManifest->get($strSection, $strManifestFile, MANIFEST_SUBKEY_GROUP);\n"
|
|
"\n\n"
|
|
"if (!defined($hTargetManifest->{$strName}{user}) ||\n"
|
|
"$strUser ne $hTargetManifest->{$strName}{user} ||\n"
|
|
"!defined($hTargetManifest->{$strName}{group}) ||\n"
|
|
"$strGroup ne $hTargetManifest->{$strName}{group})\n"
|
|
"{\n"
|
|
"&log(DETAIL, \"set ownership ${strUser}:${strGroup} on ${strOsFile}\");\n"
|
|
"\n"
|
|
"$oStorageDb->owner($strOsFile, $strUser, $strGroup);\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if ($strSection eq MANIFEST_SECTION_TARGET_LINK)\n"
|
|
"{\n"
|
|
"if ($oManifest->get($strSection, $strManifestFile, MANIFEST_SUBKEY_DESTINATION) ne\n"
|
|
"$hTargetManifest->{$strName}{link_destination})\n"
|
|
"{\n"
|
|
"&log(DETAIL, \"remove link ${strOsFile} - destination changed\");\n"
|
|
"$oStorageDb->remove($strOsFile);\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n"
|
|
"else\n"
|
|
"{\n"
|
|
"my $strMode = $oManifest->get($strSection, $strManifestFile, MANIFEST_SUBKEY_MODE);\n"
|
|
"\n"
|
|
"if ($strMode ne $hTargetManifest->{$strName}{mode})\n"
|
|
"{\n"
|
|
"&log(DETAIL, \"set mode ${strMode} on ${strOsFile}\");\n"
|
|
"\n"
|
|
"chmod(oct($strMode), $strOsFile)\n"
|
|
"or confess 'unable to set mode ${strMode} on ${strOsFile}';\n"
|
|
"}\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n"
|
|
"else\n"
|
|
"{\n"
|
|
"\n"
|
|
"if ($strSection eq MANIFEST_SECTION_TARGET_PATH)\n"
|
|
"{\n"
|
|
"&log(DETAIL, \"remove path ${strOsFile}\");\n"
|
|
"rmdir($strOsFile) or confess &log(ERROR, \"unable to delete path ${strOsFile}, is it empty?\");\n"
|
|
"\n"
|
|
"$oRemoveHash{$strSection} += 1;\n"
|
|
"}\n"
|
|
"\n"
|
|
"else\n"
|
|
"{\n"
|
|
"my $strType = (split('\\:', $strSection))[1];\n"
|
|
"\n\n\n"
|
|
"if ($oManifest->dbPathGet(undef, $strManifestFile) eq DB_FILE_RECOVERYCONF)\n"
|
|
"{\n"
|
|
"&log(DETAIL, \"preserve ${strType} ${strOsFile}\");\n"
|
|
"}\n"
|
|
"else\n"
|
|
"{\n"
|
|
"&log(DETAIL, \"remove ${strType} ${strOsFile}\");\n"
|
|
"$oStorageDb->remove($strOsFile);\n"
|
|
"\n\n"
|
|
"protocolKeepAlive();\n"
|
|
"\n"
|
|
"$oRemoveHash{$strSection} += 1;\n"
|
|
"}\n"
|
|
"}\n"
|
|
"}\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n\n"
|
|
"foreach my $strSection (sort (keys %oRemoveHash))\n"
|
|
"{\n"
|
|
"for my $strManifestFile ($oManifest->keys($strSection))\n"
|
|
"{\n"
|
|
"\n\n"
|
|
"if ($strSection ne MANIFEST_SECTION_TARGET_LINK || $strManifestFile ne $strTarget)\n"
|
|
"{\n"
|
|
"\n"
|
|
"if (index($strManifestFile, \"${strTarget}/\") == 0)\n"
|
|
"{\n"
|
|
"\n"
|
|
"$oFileChecked{$strSection}{$strManifestFile} = true;\n"
|
|
"}\n"
|
|
"}\n"
|
|
"}\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"my @stryMessage;\n"
|
|
"\n"
|
|
"foreach my $strFileType (sort (keys %oRemoveHash))\n"
|
|
"{\n"
|
|
"if ($oRemoveHash{$strFileType} > 0)\n"
|
|
"{\n"
|
|
"push(@stryMessage, \"$oRemoveHash{$strFileType} \" . (split('\\:', $strFileType))[1] .\n"
|
|
"($oRemoveHash{$strFileType} > 1 ? 's' : ''));\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n"
|
|
"if (@stryMessage)\n"
|
|
"{\n"
|
|
"&log(INFO, 'cleanup removed ' . join(', ', @stryMessage));\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"return logDebugReturn($strOperation);\n"
|
|
"}\n"
|
|
"\n\n\n\n\n\n"
|
|
"sub build\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$oManifest\n"
|
|
"\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->build', \\@_,\n"
|
|
"{name => 'oManifest'}\n"
|
|
");\n"
|
|
"\n\n"
|
|
"my $oStorageDb = storageDb();\n"
|
|
"\n\n"
|
|
"foreach my $strTarget ($oManifest->keys(MANIFEST_SECTION_BACKUP_TARGET))\n"
|
|
"{\n"
|
|
"if ($strTarget ne MANIFEST_TARGET_PGDATA)\n"
|
|
"{\n"
|
|
"my $strPath = ${$self->{oTargetPath}}{$strTarget};\n"
|
|
"\n"
|
|
"if ($oManifest->isTargetTablespace($strTarget))\n"
|
|
"{\n"
|
|
"\n"
|
|
"$strPath = dirname($strPath);\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if (!$oStorageDb->pathExists($strPath))\n"
|
|
"{\n"
|
|
"$oStorageDb->pathCreate(\n"
|
|
"$strPath, {strMode => $oManifest->get(MANIFEST_SECTION_TARGET_PATH, $strTarget, MANIFEST_SUBKEY_MODE)});\n"
|
|
"\n\n"
|
|
"my $strUser = $oManifest->get(MANIFEST_SECTION_TARGET_PATH, $strTarget, MANIFEST_SUBKEY_USER);\n"
|
|
"my $strGroup = $oManifest->get(MANIFEST_SECTION_TARGET_PATH, $strTarget, MANIFEST_SUBKEY_GROUP);\n"
|
|
"\n"
|
|
"if ($strUser ne getpwuid($<) || $strGroup ne getgrgid($())\n"
|
|
"{\n"
|
|
"$oStorageDb->owner($strPath, $strUser, $strGroup);\n"
|
|
"}\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n"
|
|
"$oManifest->remove(MANIFEST_SECTION_TARGET_PATH, $strTarget);\n"
|
|
"}\n"
|
|
"\n\n\n"
|
|
"my $iLevel = 1;\n"
|
|
"my $iFound;\n"
|
|
"\n"
|
|
"do\n"
|
|
"{\n"
|
|
"$iFound = 0;\n"
|
|
"\n"
|
|
"&log(DEBUG, \"build level ${iLevel} paths/links\");\n"
|
|
"\n\n"
|
|
"foreach my $strSection (&MANIFEST_SECTION_TARGET_PATH, &MANIFEST_SECTION_TARGET_LINK)\n"
|
|
"{\n"
|
|
"\n"
|
|
"foreach my $strName ($oManifest->keys($strSection))\n"
|
|
"{\n"
|
|
"\n"
|
|
"if ($strName eq '.')\n"
|
|
"{\n"
|
|
"next;\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"my @stryName = split('\\/', $oManifest->dbPathGet(undef, $strName));\n"
|
|
"\n\n"
|
|
"if (@stryName == $iLevel)\n"
|
|
"{\n"
|
|
"my $strDbPath = $oManifest->dbPathGet($self->{strDbClusterPath}, $strName);\n"
|
|
"\n\n\n"
|
|
"if (!$oStorageDb->pathExists($strDbPath) && !$oStorageDb->exists($strDbPath))\n"
|
|
"{\n"
|
|
"\n"
|
|
"if ($strSection eq &MANIFEST_SECTION_TARGET_PATH)\n"
|
|
"{\n"
|
|
"$oStorageDb->pathCreate(\n"
|
|
"$strDbPath, {strMode => $oManifest->get($strSection, $strName, MANIFEST_SUBKEY_MODE)});\n"
|
|
"}\n"
|
|
"\n"
|
|
"else\n"
|
|
"{\n"
|
|
"\n"
|
|
"my $strDestination = $oManifest->get($strSection, $strName, MANIFEST_SUBKEY_DESTINATION);\n"
|
|
"\n\n\n\n"
|
|
"$oStorageDb->linkCreate(\n"
|
|
"$oStorageDb->pathAbsolute(\n"
|
|
"dirname($strDbPath), $strDestination), $strDbPath,\n"
|
|
"{bRelative => (index($strDestination, '/') != 0, bIgnoreExists => true)});\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"my $strUser = $oManifest->get($strSection, $strName, MANIFEST_SUBKEY_USER);\n"
|
|
"my $strGroup = $oManifest->get($strSection, $strName, MANIFEST_SUBKEY_GROUP);\n"
|
|
"\n"
|
|
"if ($strUser ne getpwuid($<) || $strGroup ne getgrgid($())\n"
|
|
"{\n"
|
|
"$oStorageDb->owner($strDbPath, $strUser, $strGroup);\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"$iFound++;\n"
|
|
"}\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"$iLevel++;\n"
|
|
"}\n"
|
|
"while ($iFound > 0);\n"
|
|
"\n\n"
|
|
"return logDebugReturn($strOperation);\n"
|
|
"}\n"
|
|
"\n\n\n\n\n\n"
|
|
"sub recovery\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"my $strDbVersion = shift;\n"
|
|
"\n\n"
|
|
"my ($strOperation) = logDebugParam (__PACKAGE__ . '->recovery');\n"
|
|
"\n\n"
|
|
"my $strRecoveryConf = $self->{strDbClusterPath} . '/' . DB_FILE_RECOVERYCONF;\n"
|
|
"\n\n"
|
|
"my $bRecoveryConfExists = storageDb()->exists($strRecoveryConf);\n"
|
|
"\n\n"
|
|
"if (cfgOptionTest(CFGOPT_TYPE, CFGOPTVAL_RESTORE_TYPE_PRESERVE))\n"
|
|
"{\n"
|
|
"if (!$bRecoveryConfExists)\n"
|
|
"{\n"
|
|
"&log(WARN, \"recovery type is \" . cfgOption(CFGOPT_TYPE) . \" but recovery file does not exist at ${strRecoveryConf}\");\n"
|
|
"}\n"
|
|
"}\n"
|
|
"else\n"
|
|
"{\n"
|
|
"\n"
|
|
"if ($bRecoveryConfExists)\n"
|
|
"{\n"
|
|
"storageDb()->remove($strRecoveryConf);\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if (!cfgOptionTest(CFGOPT_TYPE, CFGOPTVAL_RESTORE_TYPE_NONE))\n"
|
|
"{\n"
|
|
"\n"
|
|
"my $strRecovery = '';\n"
|
|
"my $bRestoreCommandOverride = false;\n"
|
|
"\n"
|
|
"if (cfgOptionTest(CFGOPT_RECOVERY_OPTION))\n"
|
|
"{\n"
|
|
"my $oRecoveryRef = cfgOption(CFGOPT_RECOVERY_OPTION);\n"
|
|
"\n"
|
|
"foreach my $strKey (sort(keys(%$oRecoveryRef)))\n"
|
|
"{\n"
|
|
"my $strPgKey = $strKey;\n"
|
|
"$strPgKey =~ s/\\-/\\_/g;\n"
|
|
"\n"
|
|
"if ($strPgKey eq 'restore_command')\n"
|
|
"{\n"
|
|
"$bRestoreCommandOverride = true;\n"
|
|
"}\n"
|
|
"\n"
|
|
"$strRecovery .= \"${strPgKey} = '$$oRecoveryRef{$strKey}'\\n\";\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if (!$bRestoreCommandOverride)\n"
|
|
"{\n"
|
|
"$strRecovery .= \"restore_command = '\" . cfgCommandWrite(CFGCMD_ARCHIVE_GET) . \" %f \\\"%p\\\"'\\n\";\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if (cfgOptionTest(CFGOPT_TYPE, CFGOPTVAL_RESTORE_TYPE_IMMEDIATE))\n"
|
|
"{\n"
|
|
"$strRecovery .= \"recovery_target = '\" . CFGOPTVAL_RESTORE_TYPE_IMMEDIATE . \"'\\n\";\n"
|
|
"}\n"
|
|
"\n"
|
|
"elsif (!cfgOptionTest(CFGOPT_TYPE, CFGOPTVAL_RESTORE_TYPE_DEFAULT))\n"
|
|
"{\n"
|
|
"\n"
|
|
"$strRecovery .= \"recovery_target_\" . cfgOption(CFGOPT_TYPE) . \" = '\" . cfgOption(CFGOPT_TARGET) . \"'\\n\";\n"
|
|
"\n\n"
|
|
"if (cfgOption(CFGOPT_TARGET_EXCLUSIVE, false))\n"
|
|
"{\n"
|
|
"$strRecovery .= \"recovery_target_inclusive = 'false'\\n\";\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if (cfgOptionTest(CFGOPT_TARGET_ACTION))\n"
|
|
"{\n"
|
|
"my $strTargetAction = cfgOption(CFGOPT_TARGET_ACTION);\n"
|
|
"\n"
|
|
"if ($strTargetAction ne cfgOptionDefault(CFGOPT_TARGET_ACTION))\n"
|
|
"{\n"
|
|
"if ($strDbVersion >= PG_VERSION_95)\n"
|
|
"{\n"
|
|
"$strRecovery .= \"recovery_target_action = '${strTargetAction}'\\n\";\n"
|
|
"}\n"
|
|
"elsif ($strDbVersion >= PG_VERSION_91)\n"
|
|
"{\n"
|
|
"if ($strTargetAction eq CFGOPTVAL_RESTORE_TARGET_ACTION_SHUTDOWN)\n"
|
|
"{\n"
|
|
"confess &log(ERROR,\n"
|
|
"cfgOptionName(CFGOPT_TARGET_ACTION) . '=' . CFGOPTVAL_RESTORE_TARGET_ACTION_SHUTDOWN .\n"
|
|
"' is only available in PostgreSQL >= ' . PG_VERSION_95)\n"
|
|
"}\n"
|
|
"\n"
|
|
"$strRecovery .= \"pause_at_recovery_target = 'false'\\n\";\n"
|
|
"}\n"
|
|
"else\n"
|
|
"{\n"
|
|
"confess &log(ERROR,\n"
|
|
"cfgOptionName(CFGOPT_TARGET_ACTION) . ' option is only available in PostgreSQL >= ' . PG_VERSION_91)\n"
|
|
"}\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if (cfgOptionTest(CFGOPT_TARGET_TIMELINE))\n"
|
|
"{\n"
|
|
"$strRecovery .= \"recovery_target_timeline = '\" . cfgOption(CFGOPT_TARGET_TIMELINE) . \"'\\n\";\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"my $hFile;\n"
|
|
"\n"
|
|
"open($hFile, '>', $strRecoveryConf)\n"
|
|
"or confess &log(ERROR, \"unable to open ${strRecoveryConf}: $!\");\n"
|
|
"\n"
|
|
"syswrite($hFile, $strRecovery)\n"
|
|
"or confess \"unable to write section ${strRecoveryConf}: $!\";\n"
|
|
"\n"
|
|
"close($hFile)\n"
|
|
"or confess \"unable to close ${strRecoveryConf}: $!\";\n"
|
|
"\n"
|
|
"&log(INFO, \"write $strRecoveryConf\");\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"return logDebugReturn($strOperation);\n"
|
|
"}\n"
|
|
"\n\n\n\n\n\n"
|
|
"sub process\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my ($strOperation) = logDebugParam (__PACKAGE__ . '->process');\n"
|
|
"\n\n"
|
|
"my $oStorageDb = storageDb();\n"
|
|
"\n"
|
|
"if (!$oStorageDb->pathExists($self->{strDbClusterPath}))\n"
|
|
"{\n"
|
|
"confess &log(ERROR, \"\\$PGDATA directory $self->{strDbClusterPath} does not exist\");\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if ($oStorageDb->exists($self->{strDbClusterPath} . '/' . DB_FILE_POSTMASTERPID))\n"
|
|
"{\n"
|
|
"confess &log(ERROR,\n"
|
|
"\"unable to restore while PostgreSQL is running\\n\" .\n"
|
|
"\"HINT: presence of '\" . DB_FILE_POSTMASTERPID . \"' in '$self->{strDbClusterPath}' indicates PostgreSQL is running.\\n\" .\n"
|
|
"\"HINT: remove '\" . DB_FILE_POSTMASTERPID . \"' only if PostgreSQL is not running.\",\n"
|
|
"ERROR_POSTMASTER_RUNNING);\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if ((cfgOption(CFGOPT_DELTA) || cfgOption(CFGOPT_FORCE)) &&\n"
|
|
"!($oStorageDb->exists($self->{strDbClusterPath} . '/' . DB_FILE_PGVERSION) ||\n"
|
|
"$oStorageDb->exists($self->{strDbClusterPath} . '/' . FILE_MANIFEST)))\n"
|
|
"{\n"
|
|
"&log(WARN, '--delta or --force specified but unable to find \\'' . DB_FILE_PGVERSION . '\\' or \\'' . FILE_MANIFEST .\n"
|
|
"'\\' in \\'' . $self->{strDbClusterPath} . '\\' to confirm that this is a valid $PGDATA directory.' .\n"
|
|
"' --delta and --force have been disabled and if any files exist in the destination directories the restore' .\n"
|
|
"' will be aborted.');\n"
|
|
"\n"
|
|
"cfgOptionSet(CFGOPT_DELTA, false);\n"
|
|
"cfgOptionSet(CFGOPT_FORCE, false);\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"$oStorageDb->copy(\n"
|
|
"storageRepo()->openRead(STORAGE_REPO_BACKUP . qw(/) . FILE_BACKUP_INFO, {bProtocolCompress => true}),\n"
|
|
"$self->{strDbClusterPath} . '/' . FILE_BACKUP_INFO);\n"
|
|
"\n"
|
|
"my $oBackupInfo = new pgBackRest::Backup::Info($self->{strDbClusterPath}, false, undef, {oStorage => storageDb()});\n"
|
|
"\n"
|
|
"$oStorageDb->remove($self->{strDbClusterPath} . '/' . FILE_BACKUP_INFO);\n"
|
|
"\n\n"
|
|
"if ($self->{strBackupSet} eq cfgOptionDefault(CFGOPT_SET))\n"
|
|
"{\n"
|
|
"$self->{strBackupSet} = $oBackupInfo->last(CFGOPTVAL_BACKUP_TYPE_INCR);\n"
|
|
"\n"
|
|
"if (!defined($self->{strBackupSet}))\n"
|
|
"{\n"
|
|
"confess &log(ERROR, \"no backup sets to restore\", ERROR_BACKUP_SET_INVALID);\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n"
|
|
"else\n"
|
|
"{\n"
|
|
"if (!$oBackupInfo->current($self->{strBackupSet}))\n"
|
|
"{\n"
|
|
"confess &log(ERROR, \"backup set $self->{strBackupSet} is not valid\", ERROR_BACKUP_SET_INVALID);\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"&log(INFO, \"restore backup set \" . $self->{strBackupSet});\n"
|
|
"\n\n"
|
|
"my $oManifest = $self->manifestLoad($oBackupInfo->cipherPassSub());\n"
|
|
"\n\n\n"
|
|
"$oStorageDb->remove(\n"
|
|
"$oManifest->dbPathGet(\n"
|
|
"$oManifest->get(MANIFEST_SECTION_BACKUP_TARGET, MANIFEST_TARGET_PGDATA, MANIFEST_SUBKEY_PATH),\n"
|
|
"MANIFEST_FILE_PGCONTROL));\n"
|
|
"\n\n"
|
|
"$self->clean($oManifest);\n"
|
|
"\n\n"
|
|
"$self->build($oManifest);\n"
|
|
"\n\n"
|
|
"my $strCurrentUser = getpwuid($<);\n"
|
|
"my $strCurrentGroup = getgrgid($();\n"
|
|
"\n\n"
|
|
"my $strDbFilter;\n"
|
|
"\n"
|
|
"if (cfgOptionTest(CFGOPT_DB_INCLUDE))\n"
|
|
"{\n"
|
|
"\n"
|
|
"my %oDbList;\n"
|
|
"\n"
|
|
"foreach my $strFile ($oManifest->keys(MANIFEST_SECTION_TARGET_FILE))\n"
|
|
"{\n"
|
|
"my $strTblspcRegEx;\n"
|
|
"if ($oManifest->dbVersion() < PG_VERSION_90)\n"
|
|
"{\n"
|
|
"$strTblspcRegEx = '^' . MANIFEST_TARGET_PGTBLSPC . '\\/[0-9]+\\/[0-9]+\\/PG\\_VERSION';\n"
|
|
"}\n"
|
|
"else\n"
|
|
"{\n"
|
|
"$strTblspcRegEx = '^' . MANIFEST_TARGET_PGTBLSPC .\n"
|
|
"'\\/[0-9]+\\/'.$oManifest->tablespacePathGet().'\\/[0-9]+\\/PG\\_VERSION';\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if ($strFile =~ ('^' . MANIFEST_TARGET_PGDATA . '\\/base\\/[0-9]+\\/PG\\_VERSION') ||\n"
|
|
"$strFile =~ ($strTblspcRegEx))\n"
|
|
"{\n"
|
|
"my $lDbId = basename(dirname($strFile));\n"
|
|
"\n"
|
|
"$oDbList{$lDbId} = true;\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if (keys(%oDbList) == 0)\n"
|
|
"{\n"
|
|
"confess &log(ASSERT, 'no databases for include/exclude -- does not look like a valid cluster');\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"&log(DETAIL, 'databases for include/exclude (' . join(', ', sort(keys(%oDbList))) . ')');\n"
|
|
"\n\n"
|
|
"my $oDbInclude = cfgOption(CFGOPT_DB_INCLUDE);\n"
|
|
"\n"
|
|
"for my $strDbKey (sort(keys(%{$oDbInclude})))\n"
|
|
"{\n"
|
|
"\n"
|
|
"if (!defined($oDbList{$strDbKey}))\n"
|
|
"{\n"
|
|
"\n"
|
|
"my $lDbId = $oManifest->get(MANIFEST_SECTION_DB, $strDbKey, MANIFEST_KEY_DB_ID, false);\n"
|
|
"\n"
|
|
"if (!defined($lDbId) || !defined($oDbList{$lDbId}))\n"
|
|
"{\n"
|
|
"confess &log(ERROR, \"database to include '${strDbKey}' does not exist\", ERROR_DB_MISSING);\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"$strDbKey = $lDbId;\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if ($strDbKey < DB_USER_OBJECT_MINIMUM_ID)\n"
|
|
"{\n"
|
|
"confess &log(ERROR, \"system databases (template0, postgres, etc.) are included by default\", ERROR_DB_INVALID);\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"delete($oDbList{$strDbKey});\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"for my $strDbKey (sort(keys(%oDbList)))\n"
|
|
"{\n"
|
|
"\n"
|
|
"if ($strDbKey >= DB_USER_OBJECT_MINIMUM_ID)\n"
|
|
"{\n"
|
|
"\n"
|
|
"$strDbFilter .= (defined($strDbFilter) ? '|' : '') .\n"
|
|
"'(^' . MANIFEST_TARGET_PGDATA . '\\/base\\/' . $strDbKey . '\\/)';\n"
|
|
"\n\n"
|
|
"for my $strTarget ($oManifest->keys(MANIFEST_SECTION_BACKUP_TARGET))\n"
|
|
"{\n"
|
|
"if ($oManifest->isTargetTablespace($strTarget))\n"
|
|
"{\n"
|
|
"if ($oManifest->dbVersion() < PG_VERSION_90)\n"
|
|
"{\n"
|
|
"$strDbFilter .= '|(^' . $strTarget . '\\/' . $strDbKey . '\\/)';\n"
|
|
"}\n"
|
|
"else\n"
|
|
"{\n"
|
|
"$strDbFilter .=\n"
|
|
"'|(^' . $strTarget . '\\/' . $oManifest->tablespacePathGet() . '\\/' . $strDbKey . '\\/)';\n"
|
|
"}\n"
|
|
"}\n"
|
|
"}\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"&log(DETAIL, \"database filter: \" . (defined($strDbFilter) ? \"${strDbFilter}\" : ''));\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"my $oRestoreProcess = new pgBackRest::Protocol::Local::Process(CFGOPTVAL_LOCAL_TYPE_BACKUP);\n"
|
|
"$oRestoreProcess->hostAdd(1, cfgOption(CFGOPT_PROCESS_MAX));\n"
|
|
"\n\n"
|
|
"my $lSizeTotal = 0;\n"
|
|
"my $lSizeCurrent = 0;\n"
|
|
"\n"
|
|
"foreach my $strRepoFile (\n"
|
|
"sort {sprintf(\"%016d-%s\", $oManifest->numericGet(MANIFEST_SECTION_TARGET_FILE, $b, MANIFEST_SUBKEY_SIZE), $b) cmp\n"
|
|
"sprintf(\"%016d-%s\", $oManifest->numericGet(MANIFEST_SECTION_TARGET_FILE, $a, MANIFEST_SUBKEY_SIZE), $a)}\n"
|
|
"($oManifest->keys(MANIFEST_SECTION_TARGET_FILE, INI_SORT_NONE)))\n"
|
|
"{\n"
|
|
"\n\n"
|
|
"if ($strRepoFile eq MANIFEST_FILE_TABLESPACEMAP &&\n"
|
|
"$oManifest->get(MANIFEST_SECTION_BACKUP_DB, MANIFEST_KEY_DB_VERSION) >= PG_VERSION_95)\n"
|
|
"{\n"
|
|
"next;\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"my $strQueueKey = MANIFEST_TARGET_PGDATA;\n"
|
|
"\n\n"
|
|
"if (index($strRepoFile, DB_PATH_PGTBLSPC . '/') == 0)\n"
|
|
"{\n"
|
|
"$strQueueKey = DB_PATH_PGTBLSPC . '/' . (split('\\/', $strRepoFile))[1];\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"my $strDbFile = $oManifest->dbPathGet($self->{strDbClusterPath}, $strRepoFile);\n"
|
|
"my $lSize = $oManifest->numericGet(MANIFEST_SECTION_TARGET_FILE, $strRepoFile, MANIFEST_SUBKEY_SIZE);\n"
|
|
"\n\n"
|
|
"if ($strRepoFile eq MANIFEST_TARGET_PGDATA . '/' . DB_FILE_PGCONTROL)\n"
|
|
"{\n"
|
|
"$strDbFile .= '.' . STORAGE_TEMP_EXT;\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"$lSizeTotal += $lSize;\n"
|
|
"\n\n"
|
|
"$oRestoreProcess->queueJob(\n"
|
|
"1, $strQueueKey, $strRepoFile, OP_RESTORE_FILE,\n"
|
|
"[$strDbFile, $lSize,\n"
|
|
"$oManifest->numericGet(MANIFEST_SECTION_TARGET_FILE, $strRepoFile, MANIFEST_SUBKEY_TIMESTAMP),\n"
|
|
"$oManifest->get(MANIFEST_SECTION_TARGET_FILE, $strRepoFile, MANIFEST_SUBKEY_CHECKSUM, $lSize > 0),\n"
|
|
"defined($strDbFilter) && $strRepoFile =~ $strDbFilter && $strRepoFile !~ /\\/PG\\_VERSION$/ ? true : false,\n"
|
|
"cfgOption(CFGOPT_FORCE), $strRepoFile,\n"
|
|
"$oManifest->boolTest(MANIFEST_SECTION_BACKUP_OPTION, MANIFEST_KEY_HARDLINK, undef, true) ? undef :\n"
|
|
"$oManifest->get(MANIFEST_SECTION_TARGET_FILE, $strRepoFile, MANIFEST_SUBKEY_REFERENCE, false),\n"
|
|
"$oManifest->get(MANIFEST_SECTION_TARGET_FILE, $strRepoFile, MANIFEST_SUBKEY_MODE),\n"
|
|
"$oManifest->get(MANIFEST_SECTION_TARGET_FILE, $strRepoFile, MANIFEST_SUBKEY_USER),\n"
|
|
"$oManifest->get(MANIFEST_SECTION_TARGET_FILE, $strRepoFile, MANIFEST_SUBKEY_GROUP),\n"
|
|
"$oManifest->numericGet(MANIFEST_SECTION_BACKUP, MANIFEST_KEY_TIMESTAMP_COPY_START), cfgOption(CFGOPT_DELTA),\n"
|
|
"$self->{strBackupSet}, $oManifest->boolGet(MANIFEST_SECTION_BACKUP_OPTION, MANIFEST_KEY_COMPRESS)],\n"
|
|
"{rParamSecure => $oManifest->cipherPassSub() ? [$oManifest->cipherPassSub()] : undef});\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"while (my $hyJob = $oRestoreProcess->process())\n"
|
|
"{\n"
|
|
"foreach my $hJob (@{$hyJob})\n"
|
|
"{\n"
|
|
"($lSizeCurrent) = restoreLog(\n"
|
|
"$hJob->{iProcessId}, @{$hJob->{rParam}}[0..5], @{$hJob->{rResult}}, $lSizeTotal, $lSizeCurrent);\n"
|
|
"}\n"
|
|
"\n\n\n"
|
|
"protocolKeepAlive();\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"$self->recovery($oManifest->get(MANIFEST_SECTION_BACKUP_DB, MANIFEST_KEY_DB_VERSION));\n"
|
|
"\n\n"
|
|
"foreach my $strPath ($oManifest->keys(MANIFEST_SECTION_TARGET_PATH))\n"
|
|
"{\n"
|
|
"\n"
|
|
"next if $strPath eq MANIFEST_TARGET_PGTBLSPC;\n"
|
|
"\n"
|
|
"$oStorageDb->pathSync($oManifest->dbPathGet($self->{strDbClusterPath}, $strPath));\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"foreach my $strTarget ($oManifest->keys(MANIFEST_SECTION_BACKUP_TARGET))\n"
|
|
"{\n"
|
|
"\n"
|
|
"next if $strTarget eq MANIFEST_TARGET_PGDATA;\n"
|
|
"\n"
|
|
"$oStorageDb->pathSync(\n"
|
|
"$oStorageDb->pathAbsolute(\n"
|
|
"$self->{strDbClusterPath} . ($strTarget =~ ('^' . MANIFEST_TARGET_PGTBLSPC) ? '/' . MANIFEST_TARGET_PGTBLSPC : ''),\n"
|
|
"$oManifest->get(MANIFEST_SECTION_BACKUP_TARGET, $strTarget, MANIFEST_VALUE_PATH)));\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"&log(INFO,\n"
|
|
"'restore ' . $oManifest->dbPathGet(undef, MANIFEST_FILE_PGCONTROL) .\n"
|
|
"' (performed last to ensure aborted restores cannot be started)');\n"
|
|
"\n"
|
|
"$oStorageDb->move(\n"
|
|
"$self->{strDbClusterPath} . '/' . DB_FILE_PGCONTROL . '.' . STORAGE_TEMP_EXT,\n"
|
|
"$self->{strDbClusterPath} . '/' . DB_FILE_PGCONTROL);\n"
|
|
"\n\n"
|
|
"$oStorageDb->pathSync($self->{strDbClusterPath});\n"
|
|
"\n\n"
|
|
"$oStorageDb->remove($self->{strDbClusterPath} . '/' . FILE_MANIFEST);\n"
|
|
"\n\n"
|
|
"$oStorageDb->pathSync($self->{strDbClusterPath});\n"
|
|
"\n\n"
|
|
"return logDebugReturn($strOperation);\n"
|
|
"}\n"
|
|
"\n"
|
|
"1;\n"
|
|
},
|
|
{
|
|
.name = "pgBackRest/RestoreFile.pm",
|
|
.data =
|
|
"\n\n\n"
|
|
"package pgBackRest::RestoreFile;\n"
|
|
"\n"
|
|
"use strict;\n"
|
|
"use warnings FATAL => qw(all);\n"
|
|
"use Carp qw(confess);\n"
|
|
"\n"
|
|
"use Exporter qw(import);\n"
|
|
"our @EXPORT = qw();\n"
|
|
"use Fcntl qw(:mode);\n"
|
|
"use File::Basename qw(dirname);\n"
|
|
"use File::stat qw(lstat);\n"
|
|
"\n"
|
|
"use pgBackRest::Common::Exception;\n"
|
|
"use pgBackRest::Common::Io::Handle;\n"
|
|
"use pgBackRest::Common::Log;\n"
|
|
"use pgBackRest::Common::String;\n"
|
|
"use pgBackRest::Config::Config;\n"
|
|
"use pgBackRest::Manifest;\n"
|
|
"use pgBackRest::Protocol::Storage::Helper;\n"
|
|
"use pgBackRest::Storage::Base;\n"
|
|
"use pgBackRest::Storage::Filter::Gzip;\n"
|
|
"use pgBackRest::Storage::Filter::Sha;\n"
|
|
"use pgBackRest::Storage::Helper;\n"
|
|
"\n\n\n\n\n\n"
|
|
"sub restoreLog\n"
|
|
"{\n"
|
|
"\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$iLocalId,\n"
|
|
"$strDbFile,\n"
|
|
"$lSize,\n"
|
|
"$lModificationTime,\n"
|
|
"$strChecksum,\n"
|
|
"$bZero,\n"
|
|
"$bForce,\n"
|
|
"$bCopy,\n"
|
|
"$lSizeTotal,\n"
|
|
"$lSizeCurrent,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '::restoreLog', \\@_,\n"
|
|
"{name => 'iLocalId', required => false},\n"
|
|
"{name => 'strDbFile'},\n"
|
|
"{name => 'lSize'},\n"
|
|
"{name => 'lModificationTime'},\n"
|
|
"{name => 'strChecksum', required => false},\n"
|
|
"{name => 'bZero', required => false, default => false},\n"
|
|
"{name => 'bForce'},\n"
|
|
"{name => 'bCopy'},\n"
|
|
"{name => 'lSizeTotal'},\n"
|
|
"{name => 'lSizeCurrent'},\n"
|
|
");\n"
|
|
"\n\n"
|
|
"my $strLog;\n"
|
|
"\n"
|
|
"if (!$bCopy && !$bZero)\n"
|
|
"{\n"
|
|
"if ($bForce)\n"
|
|
"{\n"
|
|
"$strLog = 'exists and matches size ' . $lSize . ' and modification time ' . $lModificationTime;\n"
|
|
"}\n"
|
|
"else\n"
|
|
"{\n"
|
|
"$strLog = 'exists and ' . ($lSize == 0 ? 'is zero size' : 'matches backup');\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"$lSizeCurrent += $lSize;\n"
|
|
"\n"
|
|
"&log($bCopy ? INFO : DETAIL,\n"
|
|
"'restore' . ($bZero ? ' zeroed' : '') .\n"
|
|
"\" file ${strDbFile}\" . (defined($strLog) ? \" - ${strLog}\" : '') .\n"
|
|
"' (' . fileSizeFormat($lSize) .\n"
|
|
"($lSizeTotal > 0 ? ', ' . int($lSizeCurrent * 100 / $lSizeTotal) . '%' : '') . ')' .\n"
|
|
"($lSize != 0 && !$bZero ? \" checksum ${strChecksum}\" : ''), undef, undef, undef, $iLocalId);\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'lSizeCurrent', value => $lSizeCurrent, trace => true}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n"
|
|
"push @EXPORT, qw(restoreLog);\n"
|
|
"\n"
|
|
"1;\n"
|
|
},
|
|
{
|
|
.name = "pgBackRest/Stanza.pm",
|
|
.data =
|
|
"\n\n\n\n\n"
|
|
"package pgBackRest::Stanza;\n"
|
|
"\n"
|
|
"use strict;\n"
|
|
"use warnings FATAL => qw(all);\n"
|
|
"use Carp qw(confess);\n"
|
|
"use English '-no_match_vars';\n"
|
|
"\n"
|
|
"use Exporter qw(import);\n"
|
|
"our @EXPORT = qw();\n"
|
|
"\n"
|
|
"use pgBackRest::Backup::Common;\n"
|
|
"use pgBackRest::Common::Cipher;\n"
|
|
"use pgBackRest::Common::Exception;\n"
|
|
"use pgBackRest::Common::Ini;\n"
|
|
"use pgBackRest::Common::Lock;\n"
|
|
"use pgBackRest::Common::Log;\n"
|
|
"use pgBackRest::Config::Config;\n"
|
|
"use pgBackRest::Archive::Info;\n"
|
|
"use pgBackRest::Backup::Info;\n"
|
|
"use pgBackRest::Db;\n"
|
|
"use pgBackRest::DbVersion;\n"
|
|
"use pgBackRest::InfoCommon;\n"
|
|
"use pgBackRest::Manifest;\n"
|
|
"use pgBackRest::Protocol::Helper;\n"
|
|
"use pgBackRest::Protocol::Storage::Helper;\n"
|
|
"\n\n\n\n"
|
|
"my $strHintForce = \"\\nHINT: use stanza-create --force to force the stanza data to be recreated.\";\n"
|
|
"my $strInfoMissing = \" information missing\";\n"
|
|
"my $strStanzaCreateErrorMsg = \"not empty\";\n"
|
|
"my $strRepoEncryptedMsg = \" and repo is encrypted and info file(s) are missing, --force cannot be used\";\n"
|
|
"\n\n\n\n"
|
|
"sub new\n"
|
|
"{\n"
|
|
"my $class = shift;\n"
|
|
"\n\n"
|
|
"my $self = {};\n"
|
|
"bless $self, $class;\n"
|
|
"\n\n"
|
|
"my $strOperation = logDebugParam(__PACKAGE__ . '->new');\n"
|
|
"\n\n"
|
|
"if (!cfgCommandTest(CFGCMD_STANZA_DELETE))\n"
|
|
"{\n"
|
|
"($self->{oDb}) = dbObjectGet();\n"
|
|
"$self->dbInfoGet();\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'self', value => $self}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub process\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my ($strOperation) = logDebugParam(__PACKAGE__ . '->process');\n"
|
|
"\n"
|
|
"my $iResult = 0;\n"
|
|
"\n\n"
|
|
"if (cfgCommandTest(CFGCMD_STANZA_CREATE))\n"
|
|
"{\n"
|
|
"$iResult = $self->stanzaCreate();\n"
|
|
"}\n"
|
|
"\n"
|
|
"elsif (cfgCommandTest(CFGCMD_STANZA_UPGRADE))\n"
|
|
"{\n"
|
|
"$self->stanzaUpgrade();\n"
|
|
"}\n"
|
|
"\n"
|
|
"else\n"
|
|
"{\n"
|
|
"$self->stanzaDelete();\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'iResult', value => $iResult, trace => true}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n\n\n"
|
|
"sub stanzaCreate\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my ($strOperation) = logDebugParam(__PACKAGE__ . '->stanzaCreate');\n"
|
|
"\n"
|
|
"my $bContinue = true;\n"
|
|
"\n\n"
|
|
"my $strParentPathArchive = $self->parentPathGet(STORAGE_REPO_ARCHIVE);\n"
|
|
"my $strParentPathBackup = $self->parentPathGet(STORAGE_REPO_BACKUP);\n"
|
|
"\n\n"
|
|
"my @stryFileListArchive = storageRepo()->list($strParentPathArchive, {bIgnoreMissing => true});\n"
|
|
"my @stryFileListBackup = storageRepo()->list($strParentPathBackup, {bIgnoreMissing => true});\n"
|
|
"\n\n\n\n"
|
|
"if (@stryFileListArchive || @stryFileListBackup)\n"
|
|
"{\n"
|
|
"my $strBackupInfoFile = &FILE_BACKUP_INFO;\n"
|
|
"my $strArchiveInfoFile = &ARCHIVE_INFO_FILE;\n"
|
|
"\n\n"
|
|
"my $bBackupInfoFileExists = grep(/^$strBackupInfoFile$/i, @stryFileListBackup);\n"
|
|
"my $bArchiveInfoFileExists = grep(/^$strArchiveInfoFile$/i, @stryFileListArchive);\n"
|
|
"\n\n"
|
|
"if (!$bBackupInfoFileExists)\n"
|
|
"{\n"
|
|
"$strBackupInfoFile .= &INI_COPY_EXT;\n"
|
|
"$bBackupInfoFileExists = grep(/^$strBackupInfoFile$/i, @stryFileListBackup);\n"
|
|
"}\n"
|
|
"\n"
|
|
"if (!$bArchiveInfoFileExists)\n"
|
|
"{\n"
|
|
"$strArchiveInfoFile .= &INI_COPY_EXT;\n"
|
|
"$bArchiveInfoFileExists = grep(/^$strArchiveInfoFile$/i, @stryFileListArchive);\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"my $strExistingFile = $self->existingFileName(STORAGE_REPO_BACKUP, $strParentPathBackup, &FILE_BACKUP_INFO);\n"
|
|
"if (!defined($strExistingFile))\n"
|
|
"{\n"
|
|
"$strExistingFile = $self->existingFileName(STORAGE_REPO_ARCHIVE, $strParentPathArchive, &ARCHIVE_INFO_FILE);\n"
|
|
"}\n"
|
|
"\n\n\n"
|
|
"if (defined($strExistingFile) && (!storageRepo()->encryptionValid(storageRepo()->encrypted($strExistingFile))))\n"
|
|
"{\n"
|
|
"confess &log(ERROR, 'files exist - the encryption type or passphrase cannot be changed', ERROR_PATH_NOT_EMPTY);\n"
|
|
"}\n"
|
|
"\n\n\n\n\n"
|
|
"if (!$bArchiveInfoFileExists && $bBackupInfoFileExists)\n"
|
|
"{\n"
|
|
"$self->errorForce('archive' . $strInfoMissing, ERROR_FILE_MISSING, $strExistingFile, $bArchiveInfoFileExists,\n"
|
|
"$strParentPathArchive, $strParentPathBackup);\n"
|
|
"}\n"
|
|
"elsif (!$bBackupInfoFileExists && $bArchiveInfoFileExists)\n"
|
|
"{\n"
|
|
"$self->errorForce('backup' . $strInfoMissing, ERROR_FILE_MISSING, $strExistingFile, $bBackupInfoFileExists,\n"
|
|
"$strParentPathArchive, $strParentPathBackup);\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"else\n"
|
|
"{\n"
|
|
"$self->errorForce(\n"
|
|
"(@stryFileListBackup ? 'backup directory ' : '') .\n"
|
|
"((@stryFileListBackup && @stryFileListArchive) ? 'and/or ' : '') .\n"
|
|
"(@stryFileListArchive ? 'archive directory ' : '') .\n"
|
|
"$strStanzaCreateErrorMsg, ERROR_PATH_NOT_EMPTY,\n"
|
|
"$strExistingFile, $bArchiveInfoFileExists, $strParentPathArchive, $strParentPathBackup);\n"
|
|
"\n\n"
|
|
"if (!cfgOption(CFGOPT_FORCE))\n"
|
|
"{\n"
|
|
"$bContinue = false;\n"
|
|
"}\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n"
|
|
"my $iResult = 0;\n"
|
|
"\n"
|
|
"if ($bContinue)\n"
|
|
"{\n"
|
|
"\n"
|
|
"my $oArchiveInfo =\n"
|
|
"$self->infoObject(STORAGE_REPO_ARCHIVE, $strParentPathArchive, {bRequired => false, bIgnoreMissing => true});\n"
|
|
"my $oBackupInfo =\n"
|
|
"$self->infoObject(STORAGE_REPO_BACKUP, $strParentPathBackup, {bRequired => false, bIgnoreMissing => true});\n"
|
|
"\n\n"
|
|
"($iResult, my $strResultMessage) = $self->infoFileCreate($oArchiveInfo);\n"
|
|
"\n"
|
|
"if ($iResult == 0)\n"
|
|
"{\n"
|
|
"\n"
|
|
"($iResult, $strResultMessage) = $self->infoFileCreate($oBackupInfo);\n"
|
|
"}\n"
|
|
"\n"
|
|
"if ($iResult != 0)\n"
|
|
"{\n"
|
|
"&log(WARN, \"unable to create stanza '\" . cfgOption(CFGOPT_STANZA) . \"'\");\n"
|
|
"confess &log(ERROR, $strResultMessage, $iResult);\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'iResult', value => $iResult, trace => true}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n\n\n\n"
|
|
"sub stanzaUpgrade\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my ($strOperation) = logDebugParam(__PACKAGE__ . '->stanzaUpgrade');\n"
|
|
"\n\n"
|
|
"my $oArchiveInfo = $self->infoObject(STORAGE_REPO_ARCHIVE, storageRepo()->pathGet(STORAGE_REPO_ARCHIVE));\n"
|
|
"my $oBackupInfo = $self->infoObject(STORAGE_REPO_BACKUP, storageRepo()->pathGet(STORAGE_REPO_BACKUP));\n"
|
|
"my $bBackupUpgraded = false;\n"
|
|
"my $bArchiveUpgraded = false;\n"
|
|
"\n\n"
|
|
"if ($self->upgradeCheck($oBackupInfo, STORAGE_REPO_BACKUP, ERROR_BACKUP_MISMATCH))\n"
|
|
"{\n"
|
|
"\n"
|
|
"my ($bReconstruct, $strWarningMsgArchive) = $oBackupInfo->reconstruct(false, false, $self->{oDb}{strDbVersion},\n"
|
|
"$self->{oDb}{ullDbSysId}, $self->{oDb}{iControlVersion}, $self->{oDb}{iCatalogVersion});\n"
|
|
"$oBackupInfo->save();\n"
|
|
"$bBackupUpgraded = true;\n"
|
|
"}\n"
|
|
"\n"
|
|
"if ($self->upgradeCheck($oArchiveInfo, STORAGE_REPO_ARCHIVE, ERROR_ARCHIVE_MISMATCH))\n"
|
|
"{\n"
|
|
"\n"
|
|
"my ($bReconstruct, $strWarningMsgArchive) = $oArchiveInfo->reconstruct($self->{oDb}{strDbVersion},\n"
|
|
"$self->{oDb}{ullDbSysId});\n"
|
|
"$oArchiveInfo->save();\n"
|
|
"$bArchiveUpgraded = true;\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if (!($bBackupUpgraded || $bArchiveUpgraded))\n"
|
|
"{\n"
|
|
"&log(INFO, \"the stanza data is already up to date\");\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"return logDebugReturn($strOperation);\n"
|
|
"}\n"
|
|
"\n\n\n\n\n\n"
|
|
"sub stanzaDelete\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my ($strOperation) = logDebugParam(__PACKAGE__ . '->stanzaDelete');\n"
|
|
"\n"
|
|
"my $strStanza = cfgOption(CFGOPT_STANZA);\n"
|
|
"my $oStorageRepo = storageRepo({strStanza => $strStanza});\n"
|
|
"\n\n"
|
|
"if ($oStorageRepo->pathExists(STORAGE_REPO_ARCHIVE) || $oStorageRepo->pathExists(STORAGE_REPO_BACKUP))\n"
|
|
"{\n"
|
|
"\n"
|
|
"if (!lockStopTest({bStanzaStopRequired => true}))\n"
|
|
"{\n"
|
|
"confess &log(ERROR, \"stop file does not exist for stanza '${strStanza}'\" .\n"
|
|
"\"\\nHINT: has the pgbackrest stop command been run on this server?\", ERROR_FILE_MISSING);\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if (!cfgOption(CFGOPT_FORCE))\n"
|
|
"{\n"
|
|
"\n"
|
|
"my ($oDbMaster, $iMasterRemoteIdx) = dbObjectGet({bMasterOnly => true});\n"
|
|
"\n\n"
|
|
"my $oStorageDbMaster = storageDb({iRemoteIdx => $iMasterRemoteIdx});\n"
|
|
"\n\n"
|
|
"if ($oStorageDbMaster->exists(DB_FILE_POSTMASTERPID))\n"
|
|
"{\n"
|
|
"confess &log(ERROR, DB_FILE_POSTMASTERPID . \" exists - looks like the postmaster is running. \" .\n"
|
|
"\"To delete stanza '${strStanza}', shutdown the postmaster for stanza '${strStanza}' and try again, \" .\n"
|
|
"\"or use --force.\", ERROR_POSTMASTER_RUNNING);\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"$oStorageRepo->remove(STORAGE_REPO_ARCHIVE . '/' . ARCHIVE_INFO_FILE, {bIgnoreMissing => true});\n"
|
|
"$oStorageRepo->remove(STORAGE_REPO_ARCHIVE . '/' . ARCHIVE_INFO_FILE . INI_COPY_EXT, {bIgnoreMissing => true});\n"
|
|
"\n\n"
|
|
"$oStorageRepo->remove(STORAGE_REPO_BACKUP . '/' . FILE_BACKUP_INFO, {bIgnoreMissing => true});\n"
|
|
"$oStorageRepo->remove(STORAGE_REPO_BACKUP . '/' . FILE_BACKUP_INFO . INI_COPY_EXT, {bIgnoreMissing => true});\n"
|
|
"\n\n"
|
|
"foreach my $strBackup ($oStorageRepo->list(\n"
|
|
"STORAGE_REPO_BACKUP, {strExpression => backupRegExpGet(true, true, true), strSortOrder => 'reverse',\n"
|
|
"bIgnoreMissing => true}))\n"
|
|
"{\n"
|
|
"$oStorageRepo->remove(STORAGE_REPO_BACKUP . \"/${strBackup}/\" . FILE_MANIFEST, {bIgnoreMissing => true});\n"
|
|
"$oStorageRepo->remove(STORAGE_REPO_BACKUP . \"/${strBackup}/\" . FILE_MANIFEST_COPY, {bIgnoreMissing => true});\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"$oStorageRepo->remove(STORAGE_REPO_ARCHIVE, {bRecurse => true, bIgnoreMissing => true});\n"
|
|
"$oStorageRepo->remove(STORAGE_REPO_BACKUP, {bRecurse => true, bIgnoreMissing => true});\n"
|
|
"\n\n"
|
|
"lockStart();\n"
|
|
"}\n"
|
|
"else\n"
|
|
"{\n"
|
|
"&log(INFO, \"stanza ${strStanza} already deleted\");\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"return logDebugReturn($strOperation);\n"
|
|
"}\n"
|
|
"\n\n\n\n\n\n"
|
|
"sub parentPathGet\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$strPathType,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->parentPathGet', \\@_,\n"
|
|
"{name => 'strPathType', trace => true},\n"
|
|
");\n"
|
|
"\n"
|
|
"my $strParentPath = storageRepo()->pathGet($strPathType);\n"
|
|
"\n\n"
|
|
"if (!storageRepo()->pathExists($strParentPath))\n"
|
|
"{\n"
|
|
"\n"
|
|
"storageRepo()->pathCreate($strPathType, {bIgnoreExists => true, bCreateParent => true});\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'strParentPath', value => $strParentPath},\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n\n\n"
|
|
"sub existingFileName\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$strPathType,\n"
|
|
"$strParentPath,\n"
|
|
"$strExcludeFile,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->existingFileName', \\@_,\n"
|
|
"{name => 'strPathType'},\n"
|
|
"{name => 'strParentPath'},\n"
|
|
"{name => 'strExcludeFile'},\n"
|
|
");\n"
|
|
"\n"
|
|
"my $hFullList = storageRepo()->manifest(storageRepo()->pathGet($strPathType), {bIgnoreMissing => true});\n"
|
|
"my $strExistingFile = undef;\n"
|
|
"\n"
|
|
"foreach my $strName (keys(%{$hFullList}))\n"
|
|
"{\n"
|
|
"if (($hFullList->{$strName}{type} eq 'f') &&\n"
|
|
"(substr($strName, 0, length($strExcludeFile)) ne $strExcludeFile))\n"
|
|
"{\n"
|
|
"$strExistingFile = $strParentPath . \"/\" . $strName;\n"
|
|
"last;\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'strExistingFile', value => $strExistingFile},\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n\n\n"
|
|
"sub errorForce\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$strMessage,\n"
|
|
"$iErrorCode,\n"
|
|
"$strFileName,\n"
|
|
"$bInfoFileExists,\n"
|
|
"$strParentPathArchive,\n"
|
|
"$strParentPathBackup,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->errorForce', \\@_,\n"
|
|
"{name => 'strMessage', trace => true},\n"
|
|
"{name => 'iErrorCode', trace => true},\n"
|
|
"{name => 'strFileName', required => false, trace => true},\n"
|
|
"{name => 'bInfoFileExists', trace => true},\n"
|
|
"{name => 'strParentPathArchive', trace => true},\n"
|
|
"{name => 'strParentPathBackup', trace => true},\n"
|
|
");\n"
|
|
"\n"
|
|
"my $bRepoEncrypted = false;\n"
|
|
"\n\n"
|
|
"if (defined($strFileName))\n"
|
|
"{\n"
|
|
"$bRepoEncrypted = storageRepo()->encrypted($strFileName);\n"
|
|
"}\n"
|
|
"elsif (defined(storageRepo()->cipherType()))\n"
|
|
"{\n"
|
|
"$bRepoEncrypted = true;\n"
|
|
"}\n"
|
|
"\n\n\n"
|
|
"if ($bRepoEncrypted && defined($strFileName) && !$bInfoFileExists)\n"
|
|
"{\n"
|
|
"confess &log(ERROR, $strMessage . $strRepoEncryptedMsg, $iErrorCode);\n"
|
|
"}\n"
|
|
"elsif (!cfgOption(CFGOPT_FORCE))\n"
|
|
"{\n"
|
|
"\n"
|
|
"if ($bInfoFileExists && $iErrorCode == ERROR_PATH_NOT_EMPTY)\n"
|
|
"{\n"
|
|
"if ($self->upgradeCheck(new pgBackRest::Backup::Info($strParentPathBackup), STORAGE_REPO_BACKUP,\n"
|
|
"ERROR_BACKUP_MISMATCH) ||\n"
|
|
"$self->upgradeCheck(new pgBackRest::Archive::Info($strParentPathArchive), STORAGE_REPO_ARCHIVE,\n"
|
|
"ERROR_ARCHIVE_MISMATCH))\n"
|
|
"{\n"
|
|
"confess &log(ERROR, \"backup info file or archive info file invalid\\n\" .\n"
|
|
"'HINT: use stanza-upgrade if the database has been upgraded or use --force', ERROR_FILE_INVALID);\n"
|
|
"}\n"
|
|
"else\n"
|
|
"{\n"
|
|
"&log(INFO, \"stanza-create was already performed\");\n"
|
|
"}\n"
|
|
"}\n"
|
|
"else\n"
|
|
"{\n"
|
|
"\n"
|
|
"confess &log(ERROR, $strMessage . $strHintForce, $iErrorCode);\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"return logDebugReturn($strOperation);\n"
|
|
"}\n"
|
|
"\n\n\n\n\n\n\n"
|
|
"sub infoObject\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$strPathType,\n"
|
|
"$strParentPath,\n"
|
|
"$bRequired,\n"
|
|
"$bIgnoreMissing,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->infoObject', \\@_,\n"
|
|
"{name => 'strPathType'},\n"
|
|
"{name => 'strParentPath'},\n"
|
|
"{name => 'bRequired', optional => true, default => true},\n"
|
|
"{name => 'bIgnoreMissing', optional => true, default => false},\n"
|
|
");\n"
|
|
"\n"
|
|
"my $iResult = 0;\n"
|
|
"my $strResultMessage;\n"
|
|
"my $oInfo;\n"
|
|
"\n\n"
|
|
"logDisable();\n"
|
|
"\n\n\n"
|
|
"eval\n"
|
|
"{\n"
|
|
"\n\n\n\n"
|
|
"my $oParamRef =\n"
|
|
"{bIgnoreMissing => $bIgnoreMissing, strCipherPassSub => defined(storageRepo()->cipherType()) ? cipherPassGen() : undef};\n"
|
|
"\n"
|
|
"$oInfo = ($strPathType eq STORAGE_REPO_BACKUP ?\n"
|
|
"new pgBackRest::Backup::Info($strParentPath, false, $bRequired, $oParamRef) :\n"
|
|
"new pgBackRest::Archive::Info($strParentPath, $bRequired, $oParamRef));\n"
|
|
"\n\n"
|
|
"logEnable();\n"
|
|
"return true;\n"
|
|
"}\n"
|
|
"or do\n"
|
|
"{\n"
|
|
"\n"
|
|
"logEnable();\n"
|
|
"$iResult = exceptionCode($EVAL_ERROR);\n"
|
|
"$strResultMessage = exceptionMessage($EVAL_ERROR);\n"
|
|
"};\n"
|
|
"\n"
|
|
"if ($iResult != 0)\n"
|
|
"{\n"
|
|
"\n\n\n"
|
|
"if ((cfgOptionValid(CFGOPT_FORCE) && !cfgOption(CFGOPT_FORCE)) ||\n"
|
|
"(!cfgOptionValid(CFGOPT_FORCE)))\n"
|
|
"{\n"
|
|
"if ($iResult == ERROR_FILE_MISSING)\n"
|
|
"{\n"
|
|
"confess &log(ERROR, cfgOptionValid(CFGOPT_FORCE) ? $strResultMessage . $strHintForce : $strResultMessage, $iResult);\n"
|
|
"}\n"
|
|
"else\n"
|
|
"{\n"
|
|
"confess &log(ERROR, $strResultMessage, $iResult);\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n"
|
|
"else\n"
|
|
"{\n"
|
|
"\n"
|
|
"if (($iResult != ERROR_FILE_MISSING) && ($iResult != ERROR_CRYPTO))\n"
|
|
"{\n"
|
|
"confess &log(ERROR, $strResultMessage, $iResult);\n"
|
|
"}\n"
|
|
"\n"
|
|
"my $oParamRef =\n"
|
|
"{bLoad => false, strCipherPassSub => defined(storageRepo()->cipherType()) ? cipherPassGen() : undef};\n"
|
|
"\n"
|
|
"$oInfo = ($strPathType eq STORAGE_REPO_BACKUP ?\n"
|
|
"new pgBackRest::Backup::Info($strParentPath, false, false, $oParamRef) :\n"
|
|
"new pgBackRest::Archive::Info($strParentPath, false, $oParamRef));\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'oInfo', value => $oInfo},\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n\n\n"
|
|
"sub infoFileCreate\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$oInfo,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->infoFileCreate', \\@_,\n"
|
|
"{name => 'oInfo', trace => true},\n"
|
|
");\n"
|
|
"\n"
|
|
"my $iResult = 0;\n"
|
|
"my $strResultMessage = undef;\n"
|
|
"my $strWarningMsgArchive = undef;\n"
|
|
"\n\n"
|
|
"logDisable();\n"
|
|
"\n"
|
|
"eval\n"
|
|
"{\n"
|
|
"\n"
|
|
"if (defined($oInfo->{strBackupClusterPath}))\n"
|
|
"{\n"
|
|
"$oInfo->reconstruct(false, false, $self->{oDb}{strDbVersion}, $self->{oDb}{ullDbSysId}, $self->{oDb}{iControlVersion},\n"
|
|
"$self->{oDb}{iCatalogVersion});\n"
|
|
"}\n"
|
|
"\n"
|
|
"else\n"
|
|
"{\n"
|
|
"$strWarningMsgArchive = $oInfo->reconstruct($self->{oDb}{strDbVersion}, $self->{oDb}{ullDbSysId});\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"logEnable();\n"
|
|
"return true;\n"
|
|
"}\n"
|
|
"or do\n"
|
|
"{\n"
|
|
"\n"
|
|
"logEnable();\n"
|
|
"$iResult = exceptionCode($EVAL_ERROR);\n"
|
|
"$strResultMessage = exceptionMessage($EVAL_ERROR);\n"
|
|
"};\n"
|
|
"\n\n"
|
|
"if ($iResult == 0)\n"
|
|
"{\n"
|
|
"$oInfo->save();\n"
|
|
"\n\n"
|
|
"storageRepo()->pathSync(\n"
|
|
"defined($oInfo->{strArchiveClusterPath}) ? $oInfo->{strArchiveClusterPath} : $oInfo->{strBackupClusterPath});\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if (defined($strWarningMsgArchive))\n"
|
|
"{\n"
|
|
"&log(WARN, $strWarningMsgArchive);\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'iResult', value => $iResult},\n"
|
|
"{name => 'strResultMessage', value => $strResultMessage},\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n\n\n"
|
|
"sub dbInfoGet\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my ($strOperation) = logDebugParam(__PACKAGE__ . '->dbInfoGet');\n"
|
|
"\n\n\n\n"
|
|
"if (cfgOption(CFGOPT_ONLINE))\n"
|
|
"{\n"
|
|
"\n"
|
|
"$self->{oDb}->configValidate();\n"
|
|
"}\n"
|
|
"\n"
|
|
"($self->{oDb}{strDbVersion}, $self->{oDb}{iControlVersion}, $self->{oDb}{iCatalogVersion}, $self->{oDb}{ullDbSysId})\n"
|
|
"= $self->{oDb}->info();\n"
|
|
"\n\n"
|
|
"return logDebugReturn($strOperation);\n"
|
|
"}\n"
|
|
"\n\n\n\n\n\n"
|
|
"sub upgradeCheck\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$oInfo,\n"
|
|
"$strPathType,\n"
|
|
"$iExpectedError,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->upgradeCheck', \\@_,\n"
|
|
"{name => 'oInfo'},\n"
|
|
"{name => 'strPathType'},\n"
|
|
"{name => 'iExpectedError'},\n"
|
|
");\n"
|
|
"\n"
|
|
"my $iResult = 0;\n"
|
|
"my $strResultMessage = undef;\n"
|
|
"\n\n"
|
|
"logDisable();\n"
|
|
"\n"
|
|
"eval\n"
|
|
"{\n"
|
|
"($strPathType eq STORAGE_REPO_BACKUP)\n"
|
|
"? $oInfo->check($self->{oDb}{strDbVersion}, $self->{oDb}{iControlVersion}, $self->{oDb}{iCatalogVersion},\n"
|
|
"$self->{oDb}{ullDbSysId}, true)\n"
|
|
": $oInfo->check($self->{oDb}{strDbVersion}, $self->{oDb}{ullDbSysId}, true);\n"
|
|
"logEnable();\n"
|
|
"return true;\n"
|
|
"}\n"
|
|
"or do\n"
|
|
"{\n"
|
|
"logEnable();\n"
|
|
"\n\n"
|
|
"confess $EVAL_ERROR if (exceptionCode($EVAL_ERROR) != $iExpectedError);\n"
|
|
"\n\n"
|
|
"$iResult = exceptionCode($EVAL_ERROR);\n"
|
|
"$strResultMessage = exceptionMessage($EVAL_ERROR);\n"
|
|
"};\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'bResult', value => ($iResult == $iExpectedError ? true : false)},\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n"
|
|
"1;\n"
|
|
},
|
|
{
|
|
.name = "pgBackRest/Storage/Base.pm",
|
|
.data =
|
|
"\n\n\n"
|
|
"package pgBackRest::Storage::Base;\n"
|
|
"\n"
|
|
"use strict;\n"
|
|
"use warnings FATAL => qw(all);\n"
|
|
"use Carp qw(confess);\n"
|
|
"use English '-no_match_vars';\n"
|
|
"\n"
|
|
"use Exporter qw(import);\n"
|
|
"our @EXPORT = qw();\n"
|
|
"use File::Basename qw(dirname);\n"
|
|
"\n"
|
|
"use pgBackRest::Common::Exception;\n"
|
|
"use pgBackRest::Common::Io::Base;\n"
|
|
"use pgBackRest::Common::Log;\n"
|
|
"\n\n\n\n"
|
|
"use constant STORAGE_COMPRESS => 'compress';\n"
|
|
"push @EXPORT, qw(STORAGE_COMPRESS);\n"
|
|
"use constant STORAGE_DECOMPRESS => 'decompress';\n"
|
|
"push @EXPORT, qw(STORAGE_DECOMPRESS);\n"
|
|
"\n\n\n\n"
|
|
"use constant STORAGE_ENCRYPT => 'encrypt';\n"
|
|
"push @EXPORT, qw(STORAGE_ENCRYPT);\n"
|
|
"use constant STORAGE_DECRYPT => 'decrypt';\n"
|
|
"push @EXPORT, qw(STORAGE_DECRYPT);\n"
|
|
"use constant CIPHER_MAGIC => 'Salted__';\n"
|
|
"push @EXPORT, qw(CIPHER_MAGIC);\n"
|
|
"\n\n\n\n\n\n\n"
|
|
"use constant STORAGE_CAPABILITY_SIZE_DIFF => 'size-diff';\n"
|
|
"push @EXPORT, qw(STORAGE_CAPABILITY_SIZE_DIFF);\n"
|
|
"\n"
|
|
"use constant STORAGE_CAPABILITY_LINK => 'link';\n"
|
|
"push @EXPORT, qw(STORAGE_CAPABILITY_LINK);\n"
|
|
"use constant STORAGE_CAPABILITY_PATH_SYNC => 'path-sync';\n"
|
|
"push @EXPORT, qw(STORAGE_CAPABILITY_PATH_SYNC);\n"
|
|
"\n\n\n\n"
|
|
"sub new\n"
|
|
"{\n"
|
|
"my $class = shift;\n"
|
|
"\n\n"
|
|
"my $self = {};\n"
|
|
"bless $self, $class;\n"
|
|
"\n\n"
|
|
"(\n"
|
|
"my $strOperation,\n"
|
|
"$self->{lBufferMax},\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->new', \\@_,\n"
|
|
"{name => 'lBufferMax', optional => true, default => COMMON_IO_BUFFER_MAX, trace => true},\n"
|
|
");\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'self', value => $self}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n\n"
|
|
"sub copy\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$xSourceFile,\n"
|
|
"$xDestinationFile,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->copy', \\@_,\n"
|
|
"{name => 'xSourceFile', required => false},\n"
|
|
"{name => 'xDestinationFile', required => false},\n"
|
|
");\n"
|
|
"\n\n"
|
|
"my $bResult = false;\n"
|
|
"\n\n"
|
|
"my $oSourceFileIo =\n"
|
|
"defined($xSourceFile) ?\n"
|
|
"(ref($xSourceFile) ? $xSourceFile : $self->openRead($self->pathGet($xSourceFile))) : undef;\n"
|
|
"\n\n"
|
|
"if (defined($oSourceFileIo))\n"
|
|
"{\n"
|
|
"\n"
|
|
"my $oDestinationFileIo = ref($xDestinationFile) ? $xDestinationFile : $self->openWrite($self->pathGet($xDestinationFile));\n"
|
|
"\n\n"
|
|
"my $lSizeRead;\n"
|
|
"\n"
|
|
"do\n"
|
|
"{\n"
|
|
"\n"
|
|
"my $tBuffer = '';\n"
|
|
"\n"
|
|
"$lSizeRead = $oSourceFileIo->read(\\$tBuffer, $self->{lBufferMax});\n"
|
|
"$oDestinationFileIo->write(\\$tBuffer);\n"
|
|
"}\n"
|
|
"while ($lSizeRead != 0);\n"
|
|
"\n\n"
|
|
"$oSourceFileIo->close();\n"
|
|
"$oDestinationFileIo->close();\n"
|
|
"\n\n"
|
|
"$bResult = true;\n"
|
|
"}\n"
|
|
"\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'bResult', value => $bResult, trace => true},\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub get\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$xFile,\n"
|
|
"$strCipherPass,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->get', \\@_,\n"
|
|
"{name => 'xFile', required => false, trace => true},\n"
|
|
"{name => 'strCipherPass', optional => true, redact => true},\n"
|
|
");\n"
|
|
"\n\n\n"
|
|
"my $oFileIo = defined($xFile) ? (ref($xFile) ? $xFile : $self->openRead(\n"
|
|
"$xFile, {strCipherPass => defined($strCipherPass) ? $strCipherPass : $self->cipherPassUser()})) : undef;\n"
|
|
"\n\n"
|
|
"my $tContent;\n"
|
|
"my $lSize = 0;\n"
|
|
"\n"
|
|
"if (defined($oFileIo))\n"
|
|
"{\n"
|
|
"my $lSizeRead;\n"
|
|
"\n"
|
|
"do\n"
|
|
"{\n"
|
|
"$lSizeRead = $oFileIo->read(\\$tContent, $self->{lBufferMax});\n"
|
|
"$lSize += $lSizeRead;\n"
|
|
"}\n"
|
|
"while ($lSizeRead != 0);\n"
|
|
"\n\n"
|
|
"$oFileIo->close();\n"
|
|
"\n\n"
|
|
"if ($lSize == 0)\n"
|
|
"{\n"
|
|
"$tContent = undef;\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'rtContent', value => defined($oFileIo) ? \\$tContent : undef, trace => true},\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub pathAbsolute\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$strBasePath,\n"
|
|
"$strPath\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '::pathAbsolute', \\@_,\n"
|
|
"{name => 'strBasePath', trace => true},\n"
|
|
"{name => 'strPath', trace => true}\n"
|
|
");\n"
|
|
"\n\n"
|
|
"my $strAbsolutePath;\n"
|
|
"\n\n"
|
|
"if (index($strPath, '/') == 0)\n"
|
|
"{\n"
|
|
"$strAbsolutePath = $strPath;\n"
|
|
"}\n"
|
|
"\n"
|
|
"else\n"
|
|
"{\n"
|
|
"\n"
|
|
"if (index($strBasePath, '/') != 0 || index($strBasePath, '/..') != -1)\n"
|
|
"{\n"
|
|
"confess &log(ERROR, \"${strBasePath} is not an absolute path\", ERROR_PATH_TYPE);\n"
|
|
"}\n"
|
|
"\n"
|
|
"while (index($strPath, '..') == 0)\n"
|
|
"{\n"
|
|
"$strBasePath = dirname($strBasePath);\n"
|
|
"$strPath = substr($strPath, 2);\n"
|
|
"\n"
|
|
"if (index($strPath, '/') == 0)\n"
|
|
"{\n"
|
|
"$strPath = substr($strPath, 1);\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n"
|
|
"$strAbsolutePath = \"${strBasePath}/${strPath}\";\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if (index($strAbsolutePath, '/') != 0 || index($strAbsolutePath, '/..') != -1)\n"
|
|
"{\n"
|
|
"confess &log(ERROR, \"result ${strAbsolutePath} was not an absolute path\", ERROR_PATH_TYPE);\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'strAbsolutePath', value => $strAbsolutePath, trace => true}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub put\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$xFile,\n"
|
|
"$xContent,\n"
|
|
"$strCipherPass,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->put', \\@_,\n"
|
|
"{name => 'xFile', trace => true},\n"
|
|
"{name => 'xContent', required => false, trace => true},\n"
|
|
"{name => 'strCipherPass', optional => true, trace => true, redact => true},\n"
|
|
");\n"
|
|
"\n\n\n"
|
|
"my $oFileIo = ref($xFile) ? $xFile : $self->openWrite(\n"
|
|
"$xFile, {strCipherPass => defined($strCipherPass) ? $strCipherPass : $self->cipherPassUser()});\n"
|
|
"\n\n"
|
|
"my $lSize = defined($xContent) ? length(ref($xContent) ? $$xContent : $xContent) : 0;\n"
|
|
"\n\n"
|
|
"if ($lSize > 0)\n"
|
|
"{\n"
|
|
"$oFileIo->write(ref($xContent) ? $xContent : \\$xContent);\n"
|
|
"}\n"
|
|
"\n"
|
|
"else\n"
|
|
"{\n"
|
|
"$oFileIo->open();\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"$oFileIo->close();\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'lSize', value => $lSize, trace => true},\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n"
|
|
"1;\n"
|
|
},
|
|
{
|
|
.name = "pgBackRest/Storage/Cifs/Driver.pm",
|
|
.data =
|
|
"\n\n\n\n\n"
|
|
"package pgBackRest::Storage::Cifs::Driver;\n"
|
|
"use parent 'pgBackRest::Storage::Posix::Driver';\n"
|
|
"\n"
|
|
"use strict;\n"
|
|
"use warnings FATAL => qw(all);\n"
|
|
"use Carp qw(confess);\n"
|
|
"use English '-no_match_vars';\n"
|
|
"\n"
|
|
"use Exporter qw(import);\n"
|
|
"our @EXPORT = qw();\n"
|
|
"\n"
|
|
"use pgBackRest::Common::Log;\n"
|
|
"use pgBackRest::Storage::Base;\n"
|
|
"\n\n\n\n"
|
|
"use constant STORAGE_CIFS_DRIVER => __PACKAGE__;\n"
|
|
"push @EXPORT, qw(STORAGE_CIFS_DRIVER);\n"
|
|
"\n\n\n\n"
|
|
"sub pathSync\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$strPath,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->pathSync', \\@_,\n"
|
|
"{name => 'strPath', trace => true},\n"
|
|
");\n"
|
|
"\n\n"
|
|
"return logDebugReturn($strOperation);\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub capability {shift eq STORAGE_CAPABILITY_SIZE_DIFF ? true : false}\n"
|
|
"sub className {STORAGE_CIFS_DRIVER}\n"
|
|
"\n"
|
|
"1;\n"
|
|
},
|
|
{
|
|
.name = "pgBackRest/Storage/Filter/CipherBlock.pm",
|
|
.data =
|
|
"\n\n\n"
|
|
"package pgBackRest::Storage::Filter::CipherBlock;\n"
|
|
"use parent 'pgBackRest::Common::Io::Filter';\n"
|
|
"\n"
|
|
"use strict;\n"
|
|
"use warnings FATAL => qw(all);\n"
|
|
"use Carp qw(confess);\n"
|
|
"use English '-no_match_vars';\n"
|
|
"\n"
|
|
"use Exporter qw(import);\n"
|
|
"our @EXPORT = qw();\n"
|
|
"\n"
|
|
"use pgBackRest::Common::Exception;\n"
|
|
"use pgBackRest::Common::Io::Base;\n"
|
|
"use pgBackRest::Common::Log;\n"
|
|
"use pgBackRest::LibC qw(:crypto);\n"
|
|
"use pgBackRest::Storage::Base;\n"
|
|
"\n\n\n\n"
|
|
"use constant STORAGE_FILTER_CIPHER_BLOCK => __PACKAGE__;\n"
|
|
"push @EXPORT, qw(STORAGE_FILTER_CIPHER_BLOCK);\n"
|
|
"\n\n\n\n"
|
|
"sub new\n"
|
|
"{\n"
|
|
"my $class = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$oParent,\n"
|
|
"$strCipherType,\n"
|
|
"$tCipherPass,\n"
|
|
"$strMode,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->new', \\@_,\n"
|
|
"{name => 'oParent', trace => true},\n"
|
|
"{name => 'strCipherType', trace => true},\n"
|
|
"{name => 'tCipherPass', trace => true},\n"
|
|
"{name => 'strMode', optional => true, default => STORAGE_ENCRYPT, trace => true},\n"
|
|
");\n"
|
|
"\n\n"
|
|
"my $self = $class->SUPER::new($oParent);\n"
|
|
"bless $self, $class;\n"
|
|
"\n\n"
|
|
"$self->{strMode} = $strMode;\n"
|
|
"\n"
|
|
"if (!($self->{strMode} eq STORAGE_ENCRYPT || $self->{strMode} eq STORAGE_DECRYPT))\n"
|
|
"{\n"
|
|
"confess &log(ASSERT, \"unknown cipher mode: $self->{strMode}\");\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"$self->{bWrite} = false;\n"
|
|
"\n\n"
|
|
"$self->{oCipher} = new pgBackRest::LibC::Cipher::Block(\n"
|
|
"$self->{strMode} eq STORAGE_ENCRYPT ? CIPHER_MODE_ENCRYPT : CIPHER_MODE_DECRYPT, $strCipherType, $tCipherPass,\n"
|
|
"length($tCipherPass));\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'self', value => $self}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub read\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"my $rtBuffer = shift;\n"
|
|
"my $iSize = shift;\n"
|
|
"\n\n"
|
|
"return 0 if $self->eof();\n"
|
|
"\n\n"
|
|
"my $tBufferRead = '';\n"
|
|
"my $iBufferReadSize = 0;\n"
|
|
"\n"
|
|
"do\n"
|
|
"{\n"
|
|
"\n"
|
|
"my $tCipherBuffer;\n"
|
|
"my $iActualSize = $self->SUPER::read(\\$tCipherBuffer, $iSize);\n"
|
|
"\n\n"
|
|
"if ($iActualSize > 0)\n"
|
|
"{\n"
|
|
"$tBufferRead .= $self->{oCipher}->process($tCipherBuffer);\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if ($self->eof())\n"
|
|
"{\n"
|
|
"$tBufferRead .= $self->{oCipher}->flush();\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"$iBufferReadSize = length($tBufferRead);\n"
|
|
"}\n"
|
|
"while ($iBufferReadSize < $iSize && !$self->eof());\n"
|
|
"\n\n"
|
|
"$$rtBuffer .= $tBufferRead;\n"
|
|
"\n\n"
|
|
"return $iBufferReadSize;\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub write\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"my $rtBuffer = shift;\n"
|
|
"\n\n"
|
|
"$self->{bWrite} = true;\n"
|
|
"\n\n"
|
|
"my $tCipherBuffer;\n"
|
|
"\n"
|
|
"if (defined($$rtBuffer))\n"
|
|
"{\n"
|
|
"$tCipherBuffer = $self->{oCipher}->process($$rtBuffer);\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"$self->SUPER::write(\\$tCipherBuffer);\n"
|
|
"\n"
|
|
"return length($$rtBuffer);\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub close\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"if ($self->{oCipher})\n"
|
|
"{\n"
|
|
"\n"
|
|
"if ($self->{bWrite})\n"
|
|
"{\n"
|
|
"my $tCipherBuffer = $self->{oCipher}->flush();\n"
|
|
"$self->SUPER::write(\\$tCipherBuffer);\n"
|
|
"}\n"
|
|
"\n"
|
|
"undef($self->{oCipher});\n"
|
|
"\n\n"
|
|
"return $self->SUPER::close();\n"
|
|
"}\n"
|
|
"\n"
|
|
"return false;\n"
|
|
"}\n"
|
|
"\n"
|
|
"1;\n"
|
|
},
|
|
{
|
|
.name = "pgBackRest/Storage/Filter/Gzip.pm",
|
|
.data =
|
|
"\n\n\n"
|
|
"package pgBackRest::Storage::Filter::Gzip;\n"
|
|
"use parent 'pgBackRest::Common::Io::Filter';\n"
|
|
"\n"
|
|
"use strict;\n"
|
|
"use warnings FATAL => qw(all);\n"
|
|
"use Carp qw(confess);\n"
|
|
"use English '-no_match_vars';\n"
|
|
"\n"
|
|
"use Compress::Raw::Zlib qw(WANT_GZIP MAX_WBITS Z_OK Z_BUF_ERROR Z_DATA_ERROR Z_STREAM_END);\n"
|
|
"use Exporter qw(import);\n"
|
|
"our @EXPORT = qw();\n"
|
|
"\n"
|
|
"use pgBackRest::Common::Exception;\n"
|
|
"use pgBackRest::Common::Io::Base;\n"
|
|
"use pgBackRest::Common::Log;\n"
|
|
"use pgBackRest::Storage::Base;\n"
|
|
"\n\n\n\n"
|
|
"use constant STORAGE_FILTER_GZIP => __PACKAGE__;\n"
|
|
"push @EXPORT, qw(STORAGE_FILTER_GZIP);\n"
|
|
"\n\n\n\n"
|
|
"sub new\n"
|
|
"{\n"
|
|
"my $class = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$oParent,\n"
|
|
"$bWantGzip,\n"
|
|
"$strCompressType,\n"
|
|
"$iLevel,\n"
|
|
"$lCompressBufferMax,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->new', \\@_,\n"
|
|
"{name => 'oParent', trace => true},\n"
|
|
"{name => 'bWantGzip', optional => true, default => true, trace => true},\n"
|
|
"{name => 'strCompressType', optional => true, default => STORAGE_COMPRESS, trace => true},\n"
|
|
"{name => 'iLevel', optional => true, default => 6, trace => true},\n"
|
|
"{name => 'lCompressBufferMax', optional => true, default => COMMON_IO_BUFFER_MAX, trace => true},\n"
|
|
");\n"
|
|
"\n\n"
|
|
"my $self = $class->SUPER::new($oParent);\n"
|
|
"bless $self, $class;\n"
|
|
"\n\n"
|
|
"$self->{bWantGzip} = $bWantGzip;\n"
|
|
"$self->{iLevel} = $iLevel;\n"
|
|
"$self->{lCompressBufferMax} = $lCompressBufferMax;\n"
|
|
"$self->{strCompressType} = $strCompressType;\n"
|
|
"\n\n"
|
|
"$self->{bWrite} = false;\n"
|
|
"\n\n"
|
|
"my $iZLibStatus;\n"
|
|
"\n"
|
|
"if ($self->{strCompressType} eq STORAGE_COMPRESS)\n"
|
|
"{\n"
|
|
"($self->{oZLib}, $iZLibStatus) = new Compress::Raw::Zlib::Deflate(\n"
|
|
"WindowBits => $self->{bWantGzip} ? WANT_GZIP : MAX_WBITS, Level => $self->{iLevel},\n"
|
|
"Bufsize => $self->{lCompressBufferMax}, AppendOutput => 1);\n"
|
|
"\n"
|
|
"$self->{tCompressedBuffer} = undef;\n"
|
|
"}\n"
|
|
"else\n"
|
|
"{\n"
|
|
"($self->{oZLib}, $iZLibStatus) = new Compress::Raw::Zlib::Inflate(\n"
|
|
"WindowBits => $self->{bWantGzip} ? WANT_GZIP : MAX_WBITS, Bufsize => $self->{lCompressBufferMax},\n"
|
|
"LimitOutput => 1, AppendOutput => 1);\n"
|
|
"\n"
|
|
"$self->{tUncompressedBuffer} = undef;\n"
|
|
"$self->{lUncompressedBufferSize} = 0;\n"
|
|
"}\n"
|
|
"\n"
|
|
"$self->errorCheck($iZLibStatus);\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'self', value => $self}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub errorCheck\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"my $iZLibStatus = shift;\n"
|
|
"\n"
|
|
"if (!($iZLibStatus == Z_OK || $iZLibStatus == Z_BUF_ERROR))\n"
|
|
"{\n"
|
|
"logErrorResult(\n"
|
|
"$self->{bWrite} ? ERROR_FILE_WRITE : ERROR_FILE_READ,\n"
|
|
"'unable to ' . ($self->{strCompressType} eq STORAGE_COMPRESS ? 'deflate' : 'inflate') . \" '\" .\n"
|
|
"$self->parent()->name() . \"'\",\n"
|
|
"$self->{oZLib}->msg());\n"
|
|
"}\n"
|
|
"\n"
|
|
"return Z_OK;\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub read\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"my $rtBuffer = shift;\n"
|
|
"my $iSize = shift;\n"
|
|
"\n"
|
|
"if ($self->{strCompressType} eq STORAGE_COMPRESS)\n"
|
|
"{\n"
|
|
"return 0 if $self->eof();\n"
|
|
"\n"
|
|
"my $lSizeBegin = defined($$rtBuffer) ? length($$rtBuffer) : 0;\n"
|
|
"my $lUncompressedSize;\n"
|
|
"my $lCompressedSize;\n"
|
|
"\n"
|
|
"do\n"
|
|
"{\n"
|
|
"my $tUncompressedBuffer;\n"
|
|
"$lUncompressedSize = $self->parent()->read(\\$tUncompressedBuffer, $iSize);\n"
|
|
"\n"
|
|
"if ($lUncompressedSize > 0)\n"
|
|
"{\n"
|
|
"$self->errorCheck($self->{oZLib}->deflate($tUncompressedBuffer, $$rtBuffer));\n"
|
|
"}\n"
|
|
"else\n"
|
|
"{\n"
|
|
"$self->errorCheck($self->{oZLib}->flush($$rtBuffer));\n"
|
|
"}\n"
|
|
"\n"
|
|
"$lCompressedSize = length($$rtBuffer) - $lSizeBegin;\n"
|
|
"}\n"
|
|
"while ($lUncompressedSize > 0 && $lCompressedSize < $iSize);\n"
|
|
"\n\n"
|
|
"return $lCompressedSize;\n"
|
|
"}\n"
|
|
"else\n"
|
|
"{\n"
|
|
"\n"
|
|
"while ($self->{lUncompressedBufferSize} < $iSize && !$self->parent()->eof())\n"
|
|
"{\n"
|
|
"if (!defined($self->{tCompressedBuffer}) || length($self->{tCompressedBuffer}) == 0)\n"
|
|
"{\n"
|
|
"$self->parent()->read(\\$self->{tCompressedBuffer}, $self->{lCompressBufferMax});\n"
|
|
"}\n"
|
|
"\n"
|
|
"my $iZLibStatus = $self->{oZLib}->inflate($self->{tCompressedBuffer}, $self->{tUncompressedBuffer});\n"
|
|
"$self->{lUncompressedBufferSize} = length($self->{tUncompressedBuffer});\n"
|
|
"\n"
|
|
"last if $iZLibStatus == Z_STREAM_END;\n"
|
|
"\n"
|
|
"$self->errorCheck($iZLibStatus);\n"
|
|
"}\n"
|
|
"\n\n\n"
|
|
"my $iActualSize = $self->{lUncompressedBufferSize} < $iSize ? $self->{lUncompressedBufferSize} : $iSize;\n"
|
|
"\n\n"
|
|
"$$rtBuffer .= substr($self->{tUncompressedBuffer}, 0, $iActualSize);\n"
|
|
"\n\n"
|
|
"$self->{tUncompressedBuffer} = substr($self->{tUncompressedBuffer}, $iActualSize);\n"
|
|
"$self->{lUncompressedBufferSize} -= $iActualSize;\n"
|
|
"\n\n"
|
|
"return $iActualSize;\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub write\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"my $rtBuffer = shift;\n"
|
|
"\n"
|
|
"$self->{bWrite} = true;\n"
|
|
"\n"
|
|
"if ($self->{strCompressType} eq STORAGE_COMPRESS)\n"
|
|
"{\n"
|
|
"\n"
|
|
"$self->errorCheck($self->{oZLib}->deflate($$rtBuffer, $self->{tCompressedBuffer}));\n"
|
|
"\n\n"
|
|
"if (length($self->{tCompressedBuffer}) > $self->{lCompressBufferMax})\n"
|
|
"{\n"
|
|
"$self->parent()->write(\\$self->{tCompressedBuffer});\n"
|
|
"$self->{tCompressedBuffer} = undef;\n"
|
|
"}\n"
|
|
"}\n"
|
|
"else\n"
|
|
"{\n"
|
|
"my $tCompressedBuffer = $$rtBuffer;\n"
|
|
"\n"
|
|
"while (length($tCompressedBuffer) > 0)\n"
|
|
"{\n"
|
|
"my $tUncompressedBuffer;\n"
|
|
"\n"
|
|
"my $iZLibStatus = $self->{oZLib}->inflate($tCompressedBuffer, $tUncompressedBuffer);\n"
|
|
"$self->parent()->write(\\$tUncompressedBuffer);\n"
|
|
"\n"
|
|
"last if $iZLibStatus == Z_STREAM_END;\n"
|
|
"\n"
|
|
"$self->errorCheck($iZLibStatus);\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"return length($$rtBuffer);\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub close\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n"
|
|
"if (defined($self->{oZLib}))\n"
|
|
"{\n"
|
|
"\n"
|
|
"if ($self->{bWrite})\n"
|
|
"{\n"
|
|
"if ($self->{strCompressType} eq STORAGE_COMPRESS)\n"
|
|
"{\n"
|
|
"\n"
|
|
"$self->errorCheck($self->{oZLib}->flush($self->{tCompressedBuffer}));\n"
|
|
"\n\n"
|
|
"$self->parent()->write(\\$self->{tCompressedBuffer});\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n"
|
|
"undef($self->{oZLib});\n"
|
|
"\n\n"
|
|
"return $self->parent()->close();\n"
|
|
"}\n"
|
|
"\n"
|
|
"return false;\n"
|
|
"}\n"
|
|
"\n"
|
|
"1;\n"
|
|
},
|
|
{
|
|
.name = "pgBackRest/Storage/Filter/Sha.pm",
|
|
.data =
|
|
"\n\n\n"
|
|
"package pgBackRest::Storage::Filter::Sha;\n"
|
|
"use parent 'pgBackRest::Common::Io::Filter';\n"
|
|
"\n"
|
|
"use strict;\n"
|
|
"use warnings FATAL => qw(all);\n"
|
|
"use Carp qw(confess);\n"
|
|
"use English '-no_match_vars';\n"
|
|
"\n"
|
|
"use Exporter qw(import);\n"
|
|
"our @EXPORT = qw();\n"
|
|
"\n"
|
|
"use pgBackRest::Common::Exception;\n"
|
|
"use pgBackRest::Common::Log;\n"
|
|
"\n\n\n\n"
|
|
"use constant STORAGE_FILTER_SHA => __PACKAGE__;\n"
|
|
"push @EXPORT, qw(STORAGE_FILTER_SHA);\n"
|
|
"\n\n\n\n"
|
|
"sub new\n"
|
|
"{\n"
|
|
"my $class = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$oParent,\n"
|
|
"$strAlgorithm,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->new', \\@_,\n"
|
|
"{name => 'oParent', trace => true},\n"
|
|
"{name => 'strAlgorithm', optional => true, default => 'sha1', trace => true},\n"
|
|
");\n"
|
|
"\n\n"
|
|
"my $self = $class->SUPER::new($oParent);\n"
|
|
"bless $self, $class;\n"
|
|
"\n\n"
|
|
"$self->{strAlgorithm} = $strAlgorithm;\n"
|
|
"\n\n"
|
|
"$self->{oSha} = new pgBackRest::LibC::Crypto::Hash($self->{strAlgorithm});\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'self', value => $self}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub read\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"my $rtBuffer = shift;\n"
|
|
"my $iSize = shift;\n"
|
|
"\n\n"
|
|
"my $tShaBuffer;\n"
|
|
"my $iActualSize = $self->parent()->read(\\$tShaBuffer, $iSize);\n"
|
|
"\n\n"
|
|
"if ($iActualSize > 0)\n"
|
|
"{\n"
|
|
"$self->{oSha}->process($tShaBuffer);\n"
|
|
"$$rtBuffer .= $tShaBuffer;\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"return $iActualSize;\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub write\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"my $rtBuffer = shift;\n"
|
|
"\n\n"
|
|
"$self->{oSha}->process($$rtBuffer);\n"
|
|
"\n\n"
|
|
"return $self->parent()->write($rtBuffer);\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub close\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n"
|
|
"if (defined($self->{oSha}))\n"
|
|
"{\n"
|
|
"\n"
|
|
"$self->resultSet(STORAGE_FILTER_SHA, $self->{oSha}->result());\n"
|
|
"\n\n"
|
|
"delete($self->{oSha});\n"
|
|
"\n\n"
|
|
"return $self->parent->close();\n"
|
|
"}\n"
|
|
"\n"
|
|
"return false;\n"
|
|
"}\n"
|
|
"\n"
|
|
"1;\n"
|
|
},
|
|
{
|
|
.name = "pgBackRest/Storage/Helper.pm",
|
|
.data =
|
|
"\n\n\n"
|
|
"package pgBackRest::Storage::Helper;\n"
|
|
"\n"
|
|
"use strict;\n"
|
|
"use warnings FATAL => qw(all);\n"
|
|
"use Carp qw(confess);\n"
|
|
"\n"
|
|
"use Exporter qw(import);\n"
|
|
"our @EXPORT = qw();\n"
|
|
"use File::Basename qw(basename);\n"
|
|
"\n"
|
|
"use pgBackRest::Common::Log;\n"
|
|
"use pgBackRest::Config::Config;\n"
|
|
"use pgBackRest::Storage::Posix::Driver;\n"
|
|
"use pgBackRest::Storage::Local;\n"
|
|
"use pgBackRest::Version;\n"
|
|
"\n\n\n\n"
|
|
"use constant STORAGE_LOCAL => '<LOCAL>';\n"
|
|
"push @EXPORT, qw(STORAGE_LOCAL);\n"
|
|
"\n\n\n\n"
|
|
"use constant COMPRESS_EXT => 'gz';\n"
|
|
"push @EXPORT, qw(COMPRESS_EXT);\n"
|
|
"\n\n\n\n"
|
|
"use constant STORAGE_TEMP_EXT => PROJECT_EXE . '.tmp';\n"
|
|
"push @EXPORT, qw(STORAGE_TEMP_EXT);\n"
|
|
"\n\n\n\n"
|
|
"my $hStorage;\n"
|
|
"\n\n\n\n\n\n\n"
|
|
"sub storageLocal\n"
|
|
"{\n"
|
|
"\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$strPath,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '::storageLocal', \\@_,\n"
|
|
"{name => 'strPath', default => '/', trace => true},\n"
|
|
");\n"
|
|
"\n\n"
|
|
"if (!defined($hStorage->{&STORAGE_LOCAL}{$strPath}))\n"
|
|
"{\n"
|
|
"\n"
|
|
"$hStorage->{&STORAGE_LOCAL}{$strPath} = new pgBackRest::Storage::Local(\n"
|
|
"$strPath, new pgBackRest::Storage::Posix::Driver(),\n"
|
|
"{strTempExtension => STORAGE_TEMP_EXT,\n"
|
|
"lBufferMax => cfgOptionValid(CFGOPT_BUFFER_SIZE, false) ? cfgOption(CFGOPT_BUFFER_SIZE, false) : undef});\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'oStorageLocal', value => $hStorage->{&STORAGE_LOCAL}{$strPath}, trace => true},\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n"
|
|
"push @EXPORT, qw(storageLocal);\n"
|
|
"\n"
|
|
"1;\n"
|
|
},
|
|
{
|
|
.name = "pgBackRest/Storage/Local.pm",
|
|
.data =
|
|
"\n\n\n\n\n"
|
|
"package pgBackRest::Storage::Local;\n"
|
|
"use parent 'pgBackRest::Storage::Base';\n"
|
|
"\n"
|
|
"use strict;\n"
|
|
"use warnings FATAL => qw(all);\n"
|
|
"use Carp qw(confess);\n"
|
|
"use English '-no_match_vars';\n"
|
|
"\n"
|
|
"use File::Basename qw(dirname);\n"
|
|
"\n"
|
|
"use pgBackRest::Common::Exception;\n"
|
|
"use pgBackRest::Common::Log;\n"
|
|
"use pgBackRest::Common::String;\n"
|
|
"use pgBackRest::Storage::Base;\n"
|
|
"use pgBackRest::Storage::Filter::Sha;\n"
|
|
"\n\n\n\n"
|
|
"sub new\n"
|
|
"{\n"
|
|
"my $class = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$strPathBase,\n"
|
|
"$oDriver,\n"
|
|
"$hRule,\n"
|
|
"$bAllowTemp,\n"
|
|
"$strTempExtension,\n"
|
|
"$strDefaultPathMode,\n"
|
|
"$strDefaultFileMode,\n"
|
|
"$lBufferMax,\n"
|
|
"$strCipherType,\n"
|
|
"$strCipherPassUser,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->new', \\@_,\n"
|
|
"{name => 'strPathBase'},\n"
|
|
"{name => 'oDriver'},\n"
|
|
"{name => 'hRule', optional => true},\n"
|
|
"{name => 'bAllowTemp', optional => true, default => true},\n"
|
|
"{name => 'strTempExtension', optional => true, default => 'tmp'},\n"
|
|
"{name => 'strDefaultPathMode', optional => true, default => '0750'},\n"
|
|
"{name => 'strDefaultFileMode', optional => true, default => '0640'},\n"
|
|
"{name => 'lBufferMax', optional => true},\n"
|
|
"{name => 'strCipherType', optional => true},\n"
|
|
"{name => 'strCipherPassUser', optional => true, redact => true},\n"
|
|
");\n"
|
|
"\n\n"
|
|
"my $self = $class->SUPER::new({lBufferMax => $lBufferMax});\n"
|
|
"bless $self, $class;\n"
|
|
"\n"
|
|
"$self->{strPathBase} = $strPathBase;\n"
|
|
"$self->{oDriver} = $oDriver;\n"
|
|
"$self->{hRule} = $hRule;\n"
|
|
"$self->{bAllowTemp} = $bAllowTemp;\n"
|
|
"$self->{strTempExtension} = $strTempExtension;\n"
|
|
"$self->{strDefaultPathMode} = $strDefaultPathMode;\n"
|
|
"$self->{strDefaultFileMode} = $strDefaultFileMode;\n"
|
|
"$self->{strCipherType} = $strCipherType;\n"
|
|
"$self->{strCipherPassUser} = $strCipherPassUser;\n"
|
|
"\n"
|
|
"if (defined($self->{strCipherType}))\n"
|
|
"{\n"
|
|
"require pgBackRest::Storage::Filter::CipherBlock;\n"
|
|
"pgBackRest::Storage::Filter::CipherBlock->import();\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"$self->driver()->tempExtensionSet($self->{strTempExtension}) if $self->driver()->can('tempExtensionSet');\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'self', value => $self}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub exists\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$strFileExp,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->exists', \\@_,\n"
|
|
"{name => 'strFileExp'},\n"
|
|
");\n"
|
|
"\n\n"
|
|
"my $bExists = $self->driver()->exists($self->pathGet($strFileExp));\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'bExists', value => $bExists}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n\n"
|
|
"sub hashSize\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$xFileExp,\n"
|
|
"$bIgnoreMissing,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->hashSize', \\@_,\n"
|
|
"{name => 'xFileExp'},\n"
|
|
"{name => 'bIgnoreMissing', optional => true, default => false},\n"
|
|
");\n"
|
|
"\n\n"
|
|
"my $strHash;\n"
|
|
"my $lSize;\n"
|
|
"\n\n"
|
|
"my $oFileIo =\n"
|
|
"defined($xFileExp) ? (ref($xFileExp) ? $xFileExp :\n"
|
|
"$self->openRead($self->pathGet($xFileExp), {bIgnoreMissing => $bIgnoreMissing})) : undef;\n"
|
|
"\n"
|
|
"if (defined($oFileIo))\n"
|
|
"{\n"
|
|
"$lSize = 0;\n"
|
|
"my $oShaIo = new pgBackRest::Storage::Filter::Sha($oFileIo);\n"
|
|
"my $lSizeRead;\n"
|
|
"\n"
|
|
"do\n"
|
|
"{\n"
|
|
"my $tContent;\n"
|
|
"$lSizeRead = $oShaIo->read(\\$tContent, $self->{lBufferMax});\n"
|
|
"$lSize += $lSizeRead;\n"
|
|
"}\n"
|
|
"while ($lSizeRead != 0);\n"
|
|
"\n\n"
|
|
"$oShaIo->close();\n"
|
|
"\n\n"
|
|
"$strHash = $oShaIo->result(STORAGE_FILTER_SHA);\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'strHash', value => $strHash},\n"
|
|
"{name => 'lSize', value => $lSize}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub info\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$strPathFileExp,\n"
|
|
"$bIgnoreMissing,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '::fileStat', \\@_,\n"
|
|
"{name => 'strPathFileExp'},\n"
|
|
"{name => 'bIgnoreMissing', optional => true, default => false},\n"
|
|
");\n"
|
|
"\n\n"
|
|
"my $oInfo = $self->driver()->info($self->pathGet($strPathFileExp), {bIgnoreMissing => $bIgnoreMissing});\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'oInfo', value => $oInfo, trace => true}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub linkCreate\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$strSourcePathFileExp,\n"
|
|
"$strDestinationLinkExp,\n"
|
|
"$bHard,\n"
|
|
"$bRelative,\n"
|
|
"$bPathCreate,\n"
|
|
"$bIgnoreExists,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->linkCreate', \\@_,\n"
|
|
"{name => 'strSourcePathFileExp'},\n"
|
|
"{name => 'strDestinationLinkExp'},\n"
|
|
"{name => 'bHard', optional=> true, default => false},\n"
|
|
"{name => 'bRelative', optional=> true, default => false},\n"
|
|
"{name => 'bPathCreate', optional=> true, default => true},\n"
|
|
"{name => 'bIgnoreExists', optional => true, default => false},\n"
|
|
");\n"
|
|
"\n\n"
|
|
"my $strSourcePathFile = $self->pathGet($strSourcePathFileExp);\n"
|
|
"my $strDestinationLink = $self->pathGet($strDestinationLinkExp);\n"
|
|
"\n\n"
|
|
"if ($bRelative)\n"
|
|
"{\n"
|
|
"\n"
|
|
"my @strySource = split('/', $strSourcePathFile);\n"
|
|
"my @stryDestination = split('/', $strDestinationLink);\n"
|
|
"\n"
|
|
"while (defined($strySource[0]) && defined($stryDestination[0]) && $strySource[0] eq $stryDestination[0])\n"
|
|
"{\n"
|
|
"shift(@strySource);\n"
|
|
"shift(@stryDestination);\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"$strSourcePathFile = '';\n"
|
|
"\n"
|
|
"for (my $iIndex = 0; $iIndex < @stryDestination - 1; $iIndex++)\n"
|
|
"{\n"
|
|
"$strSourcePathFile .= '../';\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"$strSourcePathFile .= join('/', @strySource);\n"
|
|
"\n"
|
|
"logDebugMisc\n"
|
|
"(\n"
|
|
"$strOperation, 'apply relative path',\n"
|
|
"{name => 'strSourcePathFile', value => $strSourcePathFile, trace => true}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"$self->driver()->linkCreate(\n"
|
|
"$strSourcePathFile, $strDestinationLink, {bHard => $bHard, bPathCreate => $bPathCreate, bIgnoreExists => $bIgnoreExists});\n"
|
|
"\n\n"
|
|
"return logDebugReturn($strOperation);\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub list\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$strPathExp,\n"
|
|
"$strExpression,\n"
|
|
"$strSortOrder,\n"
|
|
"$bIgnoreMissing,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->list', \\@_,\n"
|
|
"{name => 'strPathExp', required => false},\n"
|
|
"{name => 'strExpression', optional => true},\n"
|
|
"{name => 'strSortOrder', optional => true, default => 'forward'},\n"
|
|
"{name => 'bIgnoreMissing', optional => true, default => false},\n"
|
|
");\n"
|
|
"\n\n"
|
|
"my $rstryFileList = $self->driver()->list(\n"
|
|
"$self->pathGet($strPathExp), {strExpression => $strExpression, bIgnoreMissing => $bIgnoreMissing});\n"
|
|
"\n\n"
|
|
"if (defined($strExpression))\n"
|
|
"{\n"
|
|
"@{$rstryFileList} = grep(/$strExpression/i, @{$rstryFileList});\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if ($strSortOrder eq 'reverse')\n"
|
|
"{\n"
|
|
"@{$rstryFileList} = sort {$b cmp $a} @{$rstryFileList};\n"
|
|
"}\n"
|
|
"\n"
|
|
"else\n"
|
|
"{\n"
|
|
"@{$rstryFileList} = sort @{$rstryFileList};\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'stryFileList', value => $rstryFileList}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub manifest\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$strPathExp,\n"
|
|
"$strFilter,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->manifest', \\@_,\n"
|
|
"{name => 'strPathExp'},\n"
|
|
"{name => 'strFilter', optional => true, trace => true},\n"
|
|
");\n"
|
|
"\n"
|
|
"my $hManifest = $self->driver()->manifest($self->pathGet($strPathExp), {strFilter => $strFilter});\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'hManifest', value => $hManifest, trace => true}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub move\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$strSourcePathFileExp,\n"
|
|
"$strDestinationPathFileExp,\n"
|
|
"$bPathCreate,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->move', \\@_,\n"
|
|
"{name => 'strSourcePathExp'},\n"
|
|
"{name => 'strDestinationPathExp'},\n"
|
|
"{name => 'bPathCreate', optional => true, default => false, trace => true},\n"
|
|
");\n"
|
|
"\n\n"
|
|
"$self->driver()->move(\n"
|
|
"$self->pathGet($strSourcePathFileExp), $self->pathGet($strDestinationPathFileExp), {bPathCreate => $bPathCreate});\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub openRead\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$xFileExp,\n"
|
|
"$bIgnoreMissing,\n"
|
|
"$rhyFilter,\n"
|
|
"$strCipherPass,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->openRead', \\@_,\n"
|
|
"{name => 'xFileExp'},\n"
|
|
"{name => 'bIgnoreMissing', optional => true, default => false},\n"
|
|
"{name => 'rhyFilter', optional => true},\n"
|
|
"{name => 'strCipherPass', optional => true, redact => true},\n"
|
|
");\n"
|
|
"\n\n"
|
|
"my $oFileIo = $self->driver()->openRead($self->pathGet($xFileExp), {bIgnoreMissing => $bIgnoreMissing});\n"
|
|
"\n\n"
|
|
"if (defined($oFileIo))\n"
|
|
"{\n"
|
|
"\n\n"
|
|
"if (defined($self->cipherType()))\n"
|
|
"{\n"
|
|
"$oFileIo = &STORAGE_FILTER_CIPHER_BLOCK->new(\n"
|
|
"$oFileIo, $self->cipherType(), defined($strCipherPass) ? $strCipherPass : $self->cipherPassUser(),\n"
|
|
"{strMode => STORAGE_DECRYPT});\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if (defined($rhyFilter))\n"
|
|
"{\n"
|
|
"foreach my $rhFilter (@{$rhyFilter})\n"
|
|
"{\n"
|
|
"$oFileIo = $rhFilter->{strClass}->new($oFileIo, @{$rhFilter->{rxyParam}});\n"
|
|
"}\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'oFileIo', value => $oFileIo, trace => true},\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub openWrite\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$xFileExp,\n"
|
|
"$strMode,\n"
|
|
"$strUser,\n"
|
|
"$strGroup,\n"
|
|
"$lTimestamp,\n"
|
|
"$bAtomic,\n"
|
|
"$bPathCreate,\n"
|
|
"$rhyFilter,\n"
|
|
"$strCipherPass,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->openWrite', \\@_,\n"
|
|
"{name => 'xFileExp'},\n"
|
|
"{name => 'strMode', optional => true, default => $self->{strDefaultFileMode}},\n"
|
|
"{name => 'strUser', optional => true},\n"
|
|
"{name => 'strGroup', optional => true},\n"
|
|
"{name => 'lTimestamp', optional => true},\n"
|
|
"{name => 'bAtomic', optional => true, default => false},\n"
|
|
"{name => 'bPathCreate', optional => true, default => false},\n"
|
|
"{name => 'rhyFilter', optional => true},\n"
|
|
"{name => 'strCipherPass', optional => true, redact => true},\n"
|
|
");\n"
|
|
"\n\n"
|
|
"my $oFileIo = $self->driver()->openWrite($self->pathGet($xFileExp),\n"
|
|
"{strMode => $strMode, strUser => $strUser, strGroup => $strGroup, lTimestamp => $lTimestamp, bPathCreate => $bPathCreate,\n"
|
|
"bAtomic => $bAtomic});\n"
|
|
"\n\n"
|
|
"if (defined($self->cipherType()))\n"
|
|
"{\n"
|
|
"$oFileIo = &STORAGE_FILTER_CIPHER_BLOCK->new(\n"
|
|
"$oFileIo, $self->cipherType(), defined($strCipherPass) ? $strCipherPass : $self->cipherPassUser());\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if (defined($rhyFilter))\n"
|
|
"{\n"
|
|
"foreach my $rhFilter (reverse(@{$rhyFilter}))\n"
|
|
"{\n"
|
|
"$oFileIo = $rhFilter->{strClass}->new($oFileIo, @{$rhFilter->{rxyParam}});\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'oFileIo', value => $oFileIo, trace => true},\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub owner\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$strPathFileExp,\n"
|
|
"$strUser,\n"
|
|
"$strGroup\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->owner', \\@_,\n"
|
|
"{name => 'strPathFileExp'},\n"
|
|
"{name => 'strUser', required => false},\n"
|
|
"{name => 'strGroup', required => false}\n"
|
|
");\n"
|
|
"\n\n"
|
|
"$self->driver()->owner($self->pathGet($strPathFileExp), {strUser => $strUser, strGroup => $strGroup});\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub pathCreate\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$strPathExp,\n"
|
|
"$strMode,\n"
|
|
"$bIgnoreExists,\n"
|
|
"$bCreateParent,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->pathCreate', \\@_,\n"
|
|
"{name => 'strPathExp'},\n"
|
|
"{name => 'strMode', optional => true, default => $self->{strDefaultPathMode}},\n"
|
|
"{name => 'bIgnoreExists', optional => true, default => false},\n"
|
|
"{name => 'bCreateParent', optional => true, default => false},\n"
|
|
");\n"
|
|
"\n\n"
|
|
"$self->driver()->pathCreate(\n"
|
|
"$self->pathGet($strPathExp), {strMode => $strMode, bIgnoreExists => $bIgnoreExists, bCreateParent => $bCreateParent});\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub pathExists\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$strPathExp,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->pathExists', \\@_,\n"
|
|
"{name => 'strPathExp'},\n"
|
|
");\n"
|
|
"\n\n"
|
|
"my $bExists = $self->driver()->pathExists($self->pathGet($strPathExp));\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'bExists', value => $bExists}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub pathGet\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$strPathExp,\n"
|
|
"$bTemp,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->pathGet', \\@_,\n"
|
|
"{name => 'strPathExp', required => false, trace => true},\n"
|
|
"{name => 'bTemp', optional => true, default => false, trace => true},\n"
|
|
");\n"
|
|
"\n\n"
|
|
"my $strPath;\n"
|
|
"my $strFile;\n"
|
|
"\n\n"
|
|
"my $bAbsolute = false;\n"
|
|
"\n"
|
|
"if (defined($strPathExp) && index($strPathExp, qw(/)) == 0)\n"
|
|
"{\n"
|
|
"$bAbsolute = true;\n"
|
|
"$strPath = $strPathExp;\n"
|
|
"}\n"
|
|
"else\n"
|
|
"{\n"
|
|
"\n"
|
|
"if (defined($strPathExp) && index($strPathExp, qw(<)) == 0)\n"
|
|
"{\n"
|
|
"\n"
|
|
"my $iPos = index($strPathExp, qw(>));\n"
|
|
"\n"
|
|
"if ($iPos == -1)\n"
|
|
"{\n"
|
|
"confess &log(ASSERT, \"found < but not > in '${strPathExp}'\");\n"
|
|
"}\n"
|
|
"\n"
|
|
"my $strType = substr($strPathExp, 0, $iPos + 1);\n"
|
|
"\n\n"
|
|
"if ($iPos < length($strPathExp) - 1)\n"
|
|
"{\n"
|
|
"$strFile = substr($strPathExp, $iPos + 2);\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if (!defined($self->{hRule}->{$strType}))\n"
|
|
"{\n"
|
|
"confess &log(ASSERT, \"storage rule '${strType}' does not exist\");\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if (ref($self->{hRule}->{$strType}))\n"
|
|
"{\n"
|
|
"$strPath = $self->pathBase();\n"
|
|
"$strFile = $self->{hRule}{$strType}{fnRule}->($strType, $strFile, $self->{hRule}{$strType}{xData});\n"
|
|
"}\n"
|
|
"\n"
|
|
"else\n"
|
|
"{\n"
|
|
"$strPath = $self->pathBase() . ($self->pathBase() =~ /\\/$/ ? '' : qw{/}) . $self->{hRule}->{$strType};\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n"
|
|
"else\n"
|
|
"{\n"
|
|
"$strPath = $self->pathBase();\n"
|
|
"$strFile = $strPathExp;\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if ($bTemp)\n"
|
|
"{\n"
|
|
"\n"
|
|
"if (!$self->{bAllowTemp})\n"
|
|
"{\n"
|
|
"confess &log(ASSERT, \"temp file not supported for storage '\" . $self->pathBase() . \"'\");\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if (!$bAbsolute)\n"
|
|
"{\n"
|
|
"if (!defined($strFile))\n"
|
|
"{\n"
|
|
"confess &log(ASSERT, 'file part must be defined when temp file specified');\n"
|
|
"}\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"$strPath .= defined($strFile) ? ($strPath =~ /\\/$/ ? '' : qw{/}) . \"${strFile}\" : '';\n"
|
|
"\n\n"
|
|
"$strPath .= $bTemp ? \".$self->{strTempExtension}\" : '';\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'strPath', value => $strPath, trace => true}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub pathSync\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$strPathExp,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->pathSync', \\@_,\n"
|
|
"{name => 'strPathExp'},\n"
|
|
");\n"
|
|
"\n"
|
|
"$self->driver()->pathSync($self->pathGet($strPathExp));\n"
|
|
"\n\n"
|
|
"return logDebugReturn($strOperation);\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub remove\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$xstryPathFileExp,\n"
|
|
"$bIgnoreMissing,\n"
|
|
"$bRecurse,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->remove', \\@_,\n"
|
|
"{name => 'xstryPathFileExp'},\n"
|
|
"{name => 'bIgnoreMissing', optional => true, default => true},\n"
|
|
"{name => 'bRecurse', optional => true, default => false, trace => true},\n"
|
|
");\n"
|
|
"\n\n"
|
|
"my @stryPathFileExp;\n"
|
|
"\n"
|
|
"if (ref($xstryPathFileExp))\n"
|
|
"{\n"
|
|
"foreach my $strPathFileExp (@{$xstryPathFileExp})\n"
|
|
"{\n"
|
|
"push(@stryPathFileExp, $self->pathGet($strPathFileExp));\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"my $bRemoved = $self->driver()->remove(\n"
|
|
"ref($xstryPathFileExp) ? \\@stryPathFileExp : $self->pathGet($xstryPathFileExp),\n"
|
|
"{bIgnoreMissing => $bIgnoreMissing, bRecurse => $bRecurse});\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'bRemoved', value => $bRemoved}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub encrypted\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$strFileName,\n"
|
|
"$bIgnoreMissing,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->encrypted', \\@_,\n"
|
|
"{name => 'strFileName'},\n"
|
|
"{name => 'bIgnoreMissing', optional => true, default => false},\n"
|
|
");\n"
|
|
"\n"
|
|
"my $tMagicSignature;\n"
|
|
"my $bEncrypted = false;\n"
|
|
"\n\n"
|
|
"my $oFile = $self->driver()->openRead($self->pathGet($strFileName), {bIgnoreMissing => $bIgnoreMissing});\n"
|
|
"\n\n\n"
|
|
"if (!defined($oFile))\n"
|
|
"{\n"
|
|
"if (defined($self->{strCipherType}))\n"
|
|
"{\n"
|
|
"$bEncrypted = true;\n"
|
|
"}\n"
|
|
"}\n"
|
|
"else\n"
|
|
"{\n"
|
|
"\n"
|
|
"my $lSizeRead = $oFile->read(\\$tMagicSignature, length(CIPHER_MAGIC));\n"
|
|
"\n\n"
|
|
"$oFile->close();\n"
|
|
"\n\n\n"
|
|
"if (($lSizeRead > 0) && substr($tMagicSignature, 0, length(CIPHER_MAGIC)) eq CIPHER_MAGIC)\n"
|
|
"{\n"
|
|
"$bEncrypted = true;\n"
|
|
"}\n"
|
|
"\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'bEncrypted', value => $bEncrypted}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub encryptionValid\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$bEncrypted,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->encryptionValid', \\@_,\n"
|
|
"{name => 'bEncrypted'},\n"
|
|
");\n"
|
|
"\n"
|
|
"my $bValid = true;\n"
|
|
"\n\n"
|
|
"if ($bEncrypted)\n"
|
|
"{\n"
|
|
"if (!defined($self->{strCipherType}))\n"
|
|
"{\n"
|
|
"$bValid = false;\n"
|
|
"}\n"
|
|
"}\n"
|
|
"else\n"
|
|
"{\n"
|
|
"if (defined($self->{strCipherType}))\n"
|
|
"{\n"
|
|
"$bValid = false;\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'bValid', value => $bValid}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub pathBase {shift->{strPathBase}}\n"
|
|
"sub driver {shift->{oDriver}}\n"
|
|
"sub cipherType {shift->{strCipherType}}\n"
|
|
"sub cipherPassUser {shift->{strCipherPassUser}}\n"
|
|
"\n"
|
|
"1;\n"
|
|
},
|
|
{
|
|
.name = "pgBackRest/Storage/Posix/Driver.pm",
|
|
.data =
|
|
"\n\n\n\n\n"
|
|
"package pgBackRest::Storage::Posix::Driver;\n"
|
|
"\n"
|
|
"use strict;\n"
|
|
"use warnings FATAL => qw(all);\n"
|
|
"use Carp qw(confess);\n"
|
|
"use English '-no_match_vars';\n"
|
|
"\n"
|
|
"use Exporter qw(import);\n"
|
|
"our @EXPORT = qw();\n"
|
|
"use File::Basename qw(basename dirname);\n"
|
|
"use Fcntl qw(:mode);\n"
|
|
"use File::stat qw{lstat};\n"
|
|
"\n"
|
|
"use pgBackRest::Common::Exception;\n"
|
|
"use pgBackRest::Common::Log;\n"
|
|
"use pgBackRest::Storage::Base;\n"
|
|
"use pgBackRest::Storage::Posix::FileRead;\n"
|
|
"use pgBackRest::Storage::Posix::FileWrite;\n"
|
|
"\n\n\n\n"
|
|
"use constant STORAGE_POSIX_DRIVER => __PACKAGE__;\n"
|
|
"push @EXPORT, qw(STORAGE_POSIX_DRIVER);\n"
|
|
"\n\n\n\n"
|
|
"sub new\n"
|
|
"{\n"
|
|
"my $class = shift;\n"
|
|
"\n\n"
|
|
"my $self = {};\n"
|
|
"bless $self, $class;\n"
|
|
"\n\n"
|
|
"(\n"
|
|
"my $strOperation,\n"
|
|
"$self->{bFileSync},\n"
|
|
"$self->{bPathSync},\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->new', \\@_,\n"
|
|
"{name => 'bFileSync', optional => true, default => true},\n"
|
|
"{name => 'bPathSync', optional => true, default => true},\n"
|
|
");\n"
|
|
"\n\n"
|
|
"$self->{strTempExtension} = 'tmp';\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'self', value => $self, trace => true}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub exists\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$strFile,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->exists', \\@_,\n"
|
|
"{name => 'strFile', trace => true},\n"
|
|
");\n"
|
|
"\n\n"
|
|
"my $bExists = true;\n"
|
|
"my $oStat = lstat($strFile);\n"
|
|
"\n\n"
|
|
"if (defined($oStat))\n"
|
|
"{\n"
|
|
"\n"
|
|
"$bExists = !S_ISDIR($oStat->mode) ? true : false;\n"
|
|
"}\n"
|
|
"else\n"
|
|
"{\n"
|
|
"\n"
|
|
"if (!$OS_ERROR{ENOENT})\n"
|
|
"{\n"
|
|
"logErrorResult(ERROR_FILE_EXISTS, \"unable to test if file '${strFile}' exists\", $OS_ERROR);\n"
|
|
"}\n"
|
|
"\n"
|
|
"$bExists = false;\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'bExists', value => $bExists, trace => true}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub info\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$strPathFile,\n"
|
|
"$bIgnoreMissing,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->info', \\@_,\n"
|
|
"{name => 'strFile', trace => true},\n"
|
|
"{name => 'bIgnoreMissing', optional => true, default => false, trace => true},\n"
|
|
");\n"
|
|
"\n\n"
|
|
"my $oInfo = lstat($strPathFile);\n"
|
|
"\n\n"
|
|
"if (!defined($oInfo))\n"
|
|
"{\n"
|
|
"if (!($OS_ERROR{ENOENT} && $bIgnoreMissing))\n"
|
|
"{\n"
|
|
"logErrorResult($OS_ERROR{ENOENT} ? ERROR_FILE_MISSING : ERROR_FILE_OPEN, \"unable to stat '${strPathFile}'\", $OS_ERROR);\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'oInfo', value => $oInfo, trace => true}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub linkCreate\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$strSourcePathFile,\n"
|
|
"$strDestinationLink,\n"
|
|
"$bHard,\n"
|
|
"$bPathCreate,\n"
|
|
"$bIgnoreExists,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->linkCreate', \\@_,\n"
|
|
"{name => 'strSourcePathFile', trace => true},\n"
|
|
"{name => 'strDestinationLink', trace => true},\n"
|
|
"{name => 'bHard', optional=> true, default => false, trace => true},\n"
|
|
"{name => 'bPathCreate', optional=> true, default => true, trace => true},\n"
|
|
"{name => 'bIgnoreExists', optional => true, default => false, trace => true},\n"
|
|
");\n"
|
|
"\n"
|
|
"if (!($bHard ? link($strSourcePathFile, $strDestinationLink) : symlink($strSourcePathFile, $strDestinationLink)))\n"
|
|
"{\n"
|
|
"my $strMessage = \"unable to create link '${strDestinationLink}'\";\n"
|
|
"\n\n"
|
|
"if ($OS_ERROR{ENOENT})\n"
|
|
"{\n"
|
|
"\n"
|
|
"if (!$self->exists($strSourcePathFile))\n"
|
|
"{\n"
|
|
"confess &log(ERROR, \"${strMessage} because source '${strSourcePathFile}' does not exist\", ERROR_FILE_MISSING);\n"
|
|
"}\n"
|
|
"\n"
|
|
"if (!$bPathCreate)\n"
|
|
"{\n"
|
|
"confess &log(ERROR, \"${strMessage} because parent does not exist\", ERROR_PATH_MISSING);\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"$self->pathCreate(dirname($strDestinationLink), {bIgnoreExists => true, bCreateParent => true});\n"
|
|
"\n\n"
|
|
"$self->linkCreate($strSourcePathFile, $strDestinationLink, {bHard => $bHard});\n"
|
|
"}\n"
|
|
"\n"
|
|
"elsif ($OS_ERROR{EEXIST})\n"
|
|
"{\n"
|
|
"if (!$bIgnoreExists)\n"
|
|
"{\n"
|
|
"confess &log(ERROR, \"${strMessage} because it already exists\", ERROR_PATH_EXISTS);\n"
|
|
"}\n"
|
|
"}\n"
|
|
"else\n"
|
|
"{\n"
|
|
"logErrorResult(ERROR_PATH_CREATE, ${strMessage}, $OS_ERROR);\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"return logDebugReturn($strOperation);\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub linkDestination\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$strLink,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->linkDestination', \\@_,\n"
|
|
"{name => 'strLink', trace => true},\n"
|
|
");\n"
|
|
"\n\n"
|
|
"my $strLinkDestination = readlink($strLink);\n"
|
|
"\n\n"
|
|
"if (!defined($strLinkDestination))\n"
|
|
"{\n"
|
|
"logErrorResult(\n"
|
|
"$OS_ERROR{ENOENT} ? ERROR_FILE_MISSING : ERROR_FILE_OPEN, \"unable to get destination for link ${strLink}\", $OS_ERROR);\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'strLinkDestination', value => $strLinkDestination, trace => true}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub list\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$strPath,\n"
|
|
"$bIgnoreMissing,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->list', \\@_,\n"
|
|
"{name => 'strPath', trace => true},\n"
|
|
"{name => 'bIgnoreMissing', optional => true, default => false, trace => true},\n"
|
|
");\n"
|
|
"\n\n"
|
|
"my @stryFileList;\n"
|
|
"my $hPath;\n"
|
|
"\n\n"
|
|
"if (opendir($hPath, $strPath))\n"
|
|
"{\n"
|
|
"@stryFileList = grep(!/^(\\.)|(\\.\\.)$/i, readdir($hPath));\n"
|
|
"close($hPath);\n"
|
|
"}\n"
|
|
"\n"
|
|
"else\n"
|
|
"{\n"
|
|
"\n"
|
|
"if (!($OS_ERROR{ENOENT} && $bIgnoreMissing))\n"
|
|
"{\n"
|
|
"logErrorResult($OS_ERROR{ENOENT} ? ERROR_FILE_MISSING : ERROR_FILE_OPEN, \"unable to read path '${strPath}'\", $OS_ERROR);\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'stryFileList', value => \\@stryFileList, ref => true, trace => true}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub manifest\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$strPath,\n"
|
|
"$bIgnoreMissing,\n"
|
|
"$strFilter,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->manifest', \\@_,\n"
|
|
"{name => 'strPath', trace => true},\n"
|
|
"{name => 'bIgnoreMissing', optional => true, default => false, trace => true},\n"
|
|
"{name => 'strFilter', optional => true, trace => true},\n"
|
|
");\n"
|
|
"\n\n"
|
|
"my $hManifest = {};\n"
|
|
"$self->manifestRecurse($strPath, undef, 0, $hManifest, $bIgnoreMissing, $strFilter);\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'hManifest', value => $hManifest, trace => true}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n"
|
|
"sub manifestRecurse\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$strPath,\n"
|
|
"$strSubPath,\n"
|
|
"$iDepth,\n"
|
|
"$hManifest,\n"
|
|
"$bIgnoreMissing,\n"
|
|
"$strFilter,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '::manifestRecurse', \\@_,\n"
|
|
"{name => 'strPath', trace => true},\n"
|
|
"{name => 'strSubPath', required => false, trace => true},\n"
|
|
"{name => 'iDepth', default => 0, trace => true},\n"
|
|
"{name => 'hManifest', required => false, trace => true},\n"
|
|
"{name => 'bIgnoreMissing', required => false, default => false, trace => true},\n"
|
|
"{name => 'strFilter', required => false, trace => true},\n"
|
|
");\n"
|
|
"\n\n"
|
|
"my $strPathRead = $strPath . (defined($strSubPath) ? \"/${strSubPath}\" : '');\n"
|
|
"my $hPath;\n"
|
|
"\n\n"
|
|
"my $oPathInfo = $self->info($strPathRead, {bIgnoreMissing => $bIgnoreMissing});\n"
|
|
"\n"
|
|
"if (defined($oPathInfo))\n"
|
|
"{\n"
|
|
"\n"
|
|
"if ($iDepth == 0 && !S_ISDIR($oPathInfo->mode()))\n"
|
|
"{\n"
|
|
"$hManifest->{basename($strPathRead)} = $self->manifestStat($strPathRead);\n"
|
|
"}\n"
|
|
"\n"
|
|
"else\n"
|
|
"{\n"
|
|
"\n"
|
|
"my @stryFileList = @{$self->list($strPathRead, {bIgnoreMissing => $iDepth != 0})};\n"
|
|
"unshift(@stryFileList, '.');\n"
|
|
"my $hFileStat = $self->manifestList($strPathRead, \\@stryFileList, $strFilter);\n"
|
|
"\n\n"
|
|
"foreach my $strFile (keys(%{$hFileStat}))\n"
|
|
"{\n"
|
|
"my $strManifestFile = $iDepth == 0 ? $strFile : ($strSubPath . ($strFile eq qw(.) ? '' : \"/${strFile}\"));\n"
|
|
"$hManifest->{$strManifestFile} = $hFileStat->{$strFile};\n"
|
|
"\n\n"
|
|
"if ($hManifest->{$strManifestFile}{type} eq 'd' && $strFile ne qw(.))\n"
|
|
"{\n"
|
|
"$self->manifestRecurse($strPath, $strManifestFile, $iDepth + 1, $hManifest);\n"
|
|
"}\n"
|
|
"}\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"return logDebugReturn($strOperation);\n"
|
|
"}\n"
|
|
"\n"
|
|
"sub manifestList\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$strPath,\n"
|
|
"$stryFile,\n"
|
|
"$strFilter,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->manifestList', \\@_,\n"
|
|
"{name => 'strPath', trace => true},\n"
|
|
"{name => 'stryFile', trace => true},\n"
|
|
"{name => 'strFilter', required => false, trace => true},\n"
|
|
");\n"
|
|
"\n"
|
|
"my $hFileStat = {};\n"
|
|
"\n"
|
|
"foreach my $strFile (@{$stryFile})\n"
|
|
"{\n"
|
|
"if ($strFile ne '.' && defined($strFilter) && $strFilter ne $strFile)\n"
|
|
"{\n"
|
|
"next;\n"
|
|
"}\n"
|
|
"\n"
|
|
"$hFileStat->{$strFile} = $self->manifestStat(\"${strPath}\" . ($strFile eq qw(.) ? '' : \"/${strFile}\"));\n"
|
|
"\n"
|
|
"if (!defined($hFileStat->{$strFile}))\n"
|
|
"{\n"
|
|
"delete($hFileStat->{$strFile});\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'hFileStat', value => $hFileStat, trace => true}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n"
|
|
"sub manifestStat\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$strFile,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->manifestStat', \\@_,\n"
|
|
"{name => 'strFile', trace => true},\n"
|
|
");\n"
|
|
"\n\n"
|
|
"my $oStat = $self->info($strFile, {bIgnoreMissing => true});\n"
|
|
"\n\n"
|
|
"my $hFile;\n"
|
|
"\n"
|
|
"if (defined($oStat))\n"
|
|
"{\n"
|
|
"\n"
|
|
"if (S_ISREG($oStat->mode))\n"
|
|
"{\n"
|
|
"$hFile->{type} = 'f';\n"
|
|
"\n\n"
|
|
"$hFile->{size} = $oStat->size;\n"
|
|
"\n\n"
|
|
"$hFile->{modification_time} = $oStat->mtime;\n"
|
|
"}\n"
|
|
"\n"
|
|
"elsif (S_ISDIR($oStat->mode))\n"
|
|
"{\n"
|
|
"$hFile->{type} = 'd';\n"
|
|
"}\n"
|
|
"\n"
|
|
"elsif (S_ISLNK($oStat->mode))\n"
|
|
"{\n"
|
|
"$hFile->{type} = 'l';\n"
|
|
"$hFile->{link_destination} = $self->linkDestination($strFile);\n"
|
|
"}\n"
|
|
"\n"
|
|
"else\n"
|
|
"{\n"
|
|
"confess &log(ERROR, \"${strFile} is not of type directory, file, or link\", ERROR_FILE_INVALID);\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"$hFile->{user} = getpwuid($oStat->uid);\n"
|
|
"\n\n"
|
|
"$hFile->{group} = getgrgid($oStat->gid);\n"
|
|
"\n\n"
|
|
"if ($hFile->{type} ne 'l')\n"
|
|
"{\n"
|
|
"$hFile->{mode} = sprintf('%04o', S_IMODE($oStat->mode));\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'hFile', value => $hFile, trace => true}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub move\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$strSourceFile,\n"
|
|
"$strDestinationFile,\n"
|
|
"$bPathCreate,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->move', \\@_,\n"
|
|
"{name => 'strSourceFile', trace => true},\n"
|
|
"{name => 'strDestinationFile', trace => true},\n"
|
|
"{name => 'bPathCreate', default => false, trace => true},\n"
|
|
");\n"
|
|
"\n\n"
|
|
"my $strSourcePathFile = dirname($strSourceFile);\n"
|
|
"my $strDestinationPathFile = dirname($strDestinationFile);\n"
|
|
"\n\n"
|
|
"if (!rename($strSourceFile, $strDestinationFile))\n"
|
|
"{\n"
|
|
"my $strMessage = \"unable to move '${strSourceFile}'\";\n"
|
|
"\n\n"
|
|
"if ($OS_ERROR{ENOENT})\n"
|
|
"{\n"
|
|
"if (!$self->exists($strSourceFile))\n"
|
|
"{\n"
|
|
"logErrorResult(ERROR_FILE_MISSING, \"${strMessage} because it is missing\");\n"
|
|
"}\n"
|
|
"\n"
|
|
"if ($bPathCreate)\n"
|
|
"{\n"
|
|
"\n"
|
|
"$self->pathCreate($strDestinationPathFile, {bCreateParent => true, bIgnoreExists => true});\n"
|
|
"\n\n"
|
|
"$self->move($strSourceFile, $strDestinationFile);\n"
|
|
"}\n"
|
|
"else\n"
|
|
"{\n"
|
|
"logErrorResult(ERROR_PATH_MISSING, \"${strMessage} to missing path '${strDestinationPathFile}'\");\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n"
|
|
"else\n"
|
|
"{\n"
|
|
"logErrorResult(ERROR_FILE_MOVE, \"${strMessage} to '${strDestinationFile}'\", $OS_ERROR);\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"return logDebugReturn($strOperation);\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub openRead\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$strFile,\n"
|
|
"$bIgnoreMissing,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->openRead', \\@_,\n"
|
|
"{name => 'strFile', trace => true},\n"
|
|
"{name => 'bIgnoreMissing', optional => true, default => false, trace => true},\n"
|
|
");\n"
|
|
"\n"
|
|
"my $oFileIO = new pgBackRest::Storage::Posix::FileRead($self, $strFile, {bIgnoreMissing => $bIgnoreMissing});\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'oFileIO', value => $oFileIO, trace => true},\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub openWrite\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$strFile,\n"
|
|
"$strMode,\n"
|
|
"$strUser,\n"
|
|
"$strGroup,\n"
|
|
"$lTimestamp,\n"
|
|
"$bPathCreate,\n"
|
|
"$bAtomic,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->openWrite', \\@_,\n"
|
|
"{name => 'strFile', trace => true},\n"
|
|
"{name => 'strMode', optional => true, trace => true},\n"
|
|
"{name => 'strUser', optional => true, trace => true},\n"
|
|
"{name => 'strGroup', optional => true, trace => true},\n"
|
|
"{name => 'lTimestamp', optional => true, trace => true},\n"
|
|
"{name => 'bPathCreate', optional => true, trace => true},\n"
|
|
"{name => 'bAtomic', optional => true, trace => true},\n"
|
|
");\n"
|
|
"\n"
|
|
"my $oFileIO = new pgBackRest::Storage::Posix::FileWrite(\n"
|
|
"$self, $strFile,\n"
|
|
"{strMode => $strMode, strUser => $strUser, strGroup => $strGroup, lTimestamp => $lTimestamp, bPathCreate => $bPathCreate,\n"
|
|
"bAtomic => $bAtomic, bSync => $self->{bFileSync}});\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'oFileIO', value => $oFileIO, trace => true},\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub owner\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$strFilePath,\n"
|
|
"$strUser,\n"
|
|
"$strGroup,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->owner', \\@_,\n"
|
|
"{name => 'strFilePath', trace => true},\n"
|
|
"{name => 'strUser', optional => true, trace => true},\n"
|
|
"{name => 'strGroup', optional => true, trace => true},\n"
|
|
");\n"
|
|
"\n\n"
|
|
"if (defined($strUser) || defined($strGroup))\n"
|
|
"{\n"
|
|
"my $strMessage = \"unable to set ownership for '${strFilePath}'\";\n"
|
|
"my $iUserId;\n"
|
|
"my $iGroupId;\n"
|
|
"\n\n\n"
|
|
"my $oStat = $self->info($strFilePath);\n"
|
|
"\n"
|
|
"if (!defined($strUser))\n"
|
|
"{\n"
|
|
"$iUserId = $oStat->uid;\n"
|
|
"}\n"
|
|
"\n"
|
|
"if (!defined($strGroup))\n"
|
|
"{\n"
|
|
"$iGroupId = $oStat->gid;\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if (defined($strUser))\n"
|
|
"{\n"
|
|
"$iUserId = getpwnam($strUser);\n"
|
|
"\n"
|
|
"if (!defined($iUserId))\n"
|
|
"{\n"
|
|
"logErrorResult(ERROR_FILE_OWNER, \"${strMessage} because user '${strUser}' does not exist\");\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if (defined($strGroup))\n"
|
|
"{\n"
|
|
"$iGroupId = getgrnam($strGroup);\n"
|
|
"\n"
|
|
"if (!defined($iGroupId))\n"
|
|
"{\n"
|
|
"logErrorResult(ERROR_FILE_OWNER, \"${strMessage} because group '${strGroup}' does not exist\");\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if ($iUserId != $oStat->uid || $iGroupId != $oStat->gid)\n"
|
|
"{\n"
|
|
"if (!chown($iUserId, $iGroupId, $strFilePath))\n"
|
|
"{\n"
|
|
"logErrorResult(ERROR_FILE_OWNER, \"${strMessage}\", $OS_ERROR);\n"
|
|
"}\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"return logDebugReturn($strOperation);\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub pathCreate\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$strPath,\n"
|
|
"$strMode,\n"
|
|
"$bIgnoreExists,\n"
|
|
"$bCreateParent,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->pathCreate', \\@_,\n"
|
|
"{name => 'strPath', trace => true},\n"
|
|
"{name => 'strMode', optional => true, default => '0750', trace => true},\n"
|
|
"{name => 'bIgnoreExists', optional => true, default => false, trace => true},\n"
|
|
"{name => 'bCreateParent', optional => true, default => false, trace => true},\n"
|
|
");\n"
|
|
"\n\n"
|
|
"if (!mkdir($strPath, oct($strMode)))\n"
|
|
"{\n"
|
|
"my $strMessage = \"unable to create path '${strPath}'\";\n"
|
|
"\n\n"
|
|
"if ($OS_ERROR{ENOENT})\n"
|
|
"{\n"
|
|
"if (!$bCreateParent)\n"
|
|
"{\n"
|
|
"confess &log(ERROR, \"${strMessage} because parent does not exist\", ERROR_PATH_MISSING);\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"$self->pathCreate(dirname($strPath), {strMode => $strMode, bIgnoreExists => true, bCreateParent => $bCreateParent});\n"
|
|
"\n\n"
|
|
"$self->pathCreate($strPath, {strMode => $strMode, bIgnoreExists => true});\n"
|
|
"}\n"
|
|
"\n"
|
|
"elsif ($OS_ERROR{EEXIST})\n"
|
|
"{\n"
|
|
"if (!$bIgnoreExists)\n"
|
|
"{\n"
|
|
"confess &log(ERROR, \"${strMessage} because it already exists\", ERROR_PATH_EXISTS);\n"
|
|
"}\n"
|
|
"}\n"
|
|
"else\n"
|
|
"{\n"
|
|
"logErrorResult(ERROR_PATH_CREATE, ${strMessage}, $OS_ERROR);\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"return logDebugReturn($strOperation);\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub pathExists\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$strPath,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->pathExists', \\@_,\n"
|
|
"{name => 'strPath', trace => true},\n"
|
|
");\n"
|
|
"\n\n"
|
|
"my $bExists = true;\n"
|
|
"my $oStat = lstat($strPath);\n"
|
|
"\n\n"
|
|
"if (defined($oStat))\n"
|
|
"{\n"
|
|
"\n"
|
|
"$bExists = S_ISDIR($oStat->mode) ? true : false;\n"
|
|
"}\n"
|
|
"else\n"
|
|
"{\n"
|
|
"\n"
|
|
"if (!$OS_ERROR{ENOENT})\n"
|
|
"{\n"
|
|
"logErrorResult(ERROR_FILE_EXISTS, \"unable to test if path '${strPath}' exists\", $OS_ERROR);\n"
|
|
"}\n"
|
|
"\n"
|
|
"$bExists = false;\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'bExists', value => $bExists, trace => true}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub pathSync\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$strPath,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->pathSync', \\@_,\n"
|
|
"{name => 'strPath', trace => true},\n"
|
|
");\n"
|
|
"\n"
|
|
"open(my $hPath, \"<\", $strPath)\n"
|
|
"or confess &log(ERROR, \"unable to open '${strPath}' for sync\", ERROR_PATH_OPEN);\n"
|
|
"open(my $hPathDup, \">&\", $hPath)\n"
|
|
"or confess &log(ERROR, \"unable to duplicate '${strPath}' handle for sync\", ERROR_PATH_OPEN);\n"
|
|
"\n"
|
|
"$hPathDup->sync()\n"
|
|
"or confess &log(ERROR, \"unable to sync path '${strPath}'\", ERROR_PATH_SYNC);\n"
|
|
"\n"
|
|
"close($hPathDup);\n"
|
|
"close($hPath);\n"
|
|
"\n\n"
|
|
"return logDebugReturn($strOperation);\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub remove\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$strPathFile,\n"
|
|
"$bIgnoreMissing,\n"
|
|
"$bRecurse,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->remove', \\@_,\n"
|
|
"{name => 'strPathFile', trace => true},\n"
|
|
"{name => 'bIgnoreMissing', optional => true, default => false, trace => true},\n"
|
|
"{name => 'bRecurse', optional => true, default => false, trace => true},\n"
|
|
");\n"
|
|
"\n\n"
|
|
"my $bRemoved = true;\n"
|
|
"\n\n"
|
|
"if ($bRecurse)\n"
|
|
"{\n"
|
|
"\n"
|
|
"require pgBackRest::LibC;\n"
|
|
"pgBackRest::LibC->import(qw(:storage));\n"
|
|
"\n"
|
|
"storagePosixPathRemove($strPathFile, !$bIgnoreMissing, $bRecurse)\n"
|
|
"}\n"
|
|
"\n"
|
|
"else\n"
|
|
"{\n"
|
|
"foreach my $strFile (ref($strPathFile) ? @{$strPathFile} : ($strPathFile))\n"
|
|
"{\n"
|
|
"if (unlink($strFile) != 1)\n"
|
|
"{\n"
|
|
"$bRemoved = false;\n"
|
|
"\n\n"
|
|
"if (!($OS_ERROR{ENOENT} && $bIgnoreMissing))\n"
|
|
"{\n"
|
|
"logErrorResult(\n"
|
|
"$OS_ERROR{ENOENT} ? ERROR_FILE_MISSING : ERROR_FILE_OPEN, \"unable to remove file '${strFile}'\", $OS_ERROR);\n"
|
|
"}\n"
|
|
"}\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'bRemoved', value => $bRemoved, trace => true}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub capability {true}\n"
|
|
"sub className {STORAGE_POSIX_DRIVER}\n"
|
|
"sub tempExtension {shift->{strTempExtension}}\n"
|
|
"sub tempExtensionSet {my $self = shift; $self->{strTempExtension} = shift}\n"
|
|
"\n"
|
|
"1;\n"
|
|
},
|
|
{
|
|
.name = "pgBackRest/Storage/Posix/FileRead.pm",
|
|
.data =
|
|
"\n\n\n"
|
|
"package pgBackRest::Storage::Posix::FileRead;\n"
|
|
"use parent 'pgBackRest::Common::Io::Handle';\n"
|
|
"\n"
|
|
"use strict;\n"
|
|
"use warnings FATAL => qw(all);\n"
|
|
"use Carp qw(confess);\n"
|
|
"use English '-no_match_vars';\n"
|
|
"\n"
|
|
"use Fcntl qw(O_RDONLY);\n"
|
|
"\n"
|
|
"use pgBackRest::Common::Exception;\n"
|
|
"use pgBackRest::Common::Log;\n"
|
|
"\n\n\n\n"
|
|
"sub new\n"
|
|
"{\n"
|
|
"my $class = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$oDriver,\n"
|
|
"$strName,\n"
|
|
"$bIgnoreMissing,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->new', \\@_,\n"
|
|
"{name => 'oDriver', trace => true},\n"
|
|
"{name => 'strName', trace => true},\n"
|
|
"{name => 'bIgnoreMissing', optional => true, default => false, trace => true},\n"
|
|
");\n"
|
|
"\n\n"
|
|
"my $fhFile;\n"
|
|
"\n"
|
|
"if (!sysopen($fhFile, $strName, O_RDONLY))\n"
|
|
"{\n"
|
|
"if (!($OS_ERROR{ENOENT} && $bIgnoreMissing))\n"
|
|
"{\n"
|
|
"logErrorResult($OS_ERROR{ENOENT} ? ERROR_FILE_MISSING : ERROR_FILE_OPEN, \"unable to open '${strName}'\", $OS_ERROR);\n"
|
|
"}\n"
|
|
"\n"
|
|
"undef($fhFile);\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"my $self;\n"
|
|
"\n"
|
|
"if (defined($fhFile))\n"
|
|
"{\n"
|
|
"\n"
|
|
"binmode($fhFile);\n"
|
|
"\n\n"
|
|
"$self = $class->SUPER::new(\"'${strName}'\", $fhFile);\n"
|
|
"bless $self, $class;\n"
|
|
"\n\n"
|
|
"$self->{oDriver} = $oDriver;\n"
|
|
"$self->{strName} = $strName;\n"
|
|
"$self->{fhFile} = $fhFile;\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'self', value => $self, trace => true}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub close\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n"
|
|
"if (defined($self->handle()))\n"
|
|
"{\n"
|
|
"\n"
|
|
"close($self->handle());\n"
|
|
"undef($self->{fhFile});\n"
|
|
"\n\n"
|
|
"$self->SUPER::close();\n"
|
|
"}\n"
|
|
"\n"
|
|
"return true;\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub handle {shift->{fhFile}}\n"
|
|
"sub name {shift->{strName}}\n"
|
|
"\n"
|
|
"1;\n"
|
|
},
|
|
{
|
|
.name = "pgBackRest/Storage/Posix/FileWrite.pm",
|
|
.data =
|
|
"\n\n\n"
|
|
"package pgBackRest::Storage::Posix::FileWrite;\n"
|
|
"use parent 'pgBackRest::Common::Io::Handle';\n"
|
|
"\n"
|
|
"use strict;\n"
|
|
"use warnings FATAL => qw(all);\n"
|
|
"use Carp qw(confess);\n"
|
|
"use English '-no_match_vars';\n"
|
|
"\n"
|
|
"use Fcntl qw(O_RDONLY O_WRONLY O_CREAT O_TRUNC);\n"
|
|
"use File::Basename qw(dirname);\n"
|
|
"\n"
|
|
"use pgBackRest::Common::Exception;\n"
|
|
"use pgBackRest::Common::Log;\n"
|
|
"\n"
|
|
"use pgBackRest::Common::Io::Handle;\n"
|
|
"use pgBackRest::Storage::Base;\n"
|
|
"\n\n\n\n"
|
|
"sub new\n"
|
|
"{\n"
|
|
"my $class = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$oDriver,\n"
|
|
"$strName,\n"
|
|
"$strMode,\n"
|
|
"$strUser,\n"
|
|
"$strGroup,\n"
|
|
"$lTimestamp,\n"
|
|
"$bPathCreate,\n"
|
|
"$bAtomic,\n"
|
|
"$bSync,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->new', \\@_,\n"
|
|
"{name => 'oDriver', trace => true},\n"
|
|
"{name => 'strName', trace => true},\n"
|
|
"{name => 'strMode', optional => true, trace => true},\n"
|
|
"{name => 'strUser', optional => true, trace => true},\n"
|
|
"{name => 'strGroup', optional => true, trace => true},\n"
|
|
"{name => 'lTimestamp', optional => true, trace => true},\n"
|
|
"{name => 'bPathCreate', optional => true, default => false, trace => true},\n"
|
|
"{name => 'bAtomic', optional => true, default => false, trace => true},\n"
|
|
"{name => 'bSync', optional => true, default => true, trace => true},\n"
|
|
");\n"
|
|
"\n\n"
|
|
"my $self = $class->SUPER::new(\"'${strName}'\");\n"
|
|
"bless $self, $class;\n"
|
|
"\n\n"
|
|
"$self->{oDriver} = $oDriver;\n"
|
|
"$self->{strName} = $strName;\n"
|
|
"$self->{strMode} = $strMode;\n"
|
|
"$self->{strUser} = $strUser;\n"
|
|
"$self->{strGroup} = $strGroup;\n"
|
|
"$self->{lTimestamp} = $lTimestamp;\n"
|
|
"$self->{bPathCreate} = $bPathCreate;\n"
|
|
"$self->{bAtomic} = $bAtomic;\n"
|
|
"$self->{bSync} = $bSync;\n"
|
|
"\n\n"
|
|
"if ($self->{bAtomic})\n"
|
|
"{\n"
|
|
"\n"
|
|
"$self->{strNameTmp} = \"$self->{strName}.\" . $self->{oDriver}->tempExtension();\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"$self->{bOpened} = false;\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'self', value => $self, trace => true}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub open\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my $strFile = $self->{bAtomic} ? $self->{strNameTmp} : $self->{strName};\n"
|
|
"\n\n"
|
|
"if (!sysopen(\n"
|
|
"$self->{fhFile}, $strFile, O_WRONLY | O_CREAT | O_TRUNC, oct(defined($self->{strMode}) ? $self->{strMode} : '0666')))\n"
|
|
"{\n"
|
|
"\n"
|
|
"if ($OS_ERROR{ENOENT} && $self->{bPathCreate})\n"
|
|
"{\n"
|
|
"$self->{oDriver}->pathCreate(dirname($strFile), {bIgnoreExists => true, bCreateParent => true});\n"
|
|
"$self->{bPathCreate} = false;\n"
|
|
"return $self->open();\n"
|
|
"}\n"
|
|
"\n"
|
|
"logErrorResult($OS_ERROR{ENOENT} ? ERROR_PATH_MISSING : ERROR_FILE_OPEN, \"unable to open '${strFile}'\", $OS_ERROR);\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"binmode($self->{fhFile});\n"
|
|
"\n\n"
|
|
"$self->{oDriver}->owner($strFile, {strUser => $self->{strUser}, strGroup => $self->{strGroup}});\n"
|
|
"\n\n"
|
|
"$self->handleWriteSet($self->{fhFile});\n"
|
|
"\n\n"
|
|
"$self->{bOpened} = true;\n"
|
|
"\n"
|
|
"return true;\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub write\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"my $rtBuffer = shift;\n"
|
|
"\n\n"
|
|
"$self->open() if !$self->opened();\n"
|
|
"\n"
|
|
"return $self->SUPER::write($rtBuffer);\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub close\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n"
|
|
"if (defined($self->handle()))\n"
|
|
"{\n"
|
|
"\n"
|
|
"if ($self->{bSync})\n"
|
|
"{\n"
|
|
"$self->handle()->sync();\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"close($self->handle());\n"
|
|
"undef($self->{fhFile});\n"
|
|
"\n\n"
|
|
"my $strCurrentName = $self->{bAtomic} ? $self->{strNameTmp} : $self->{strName};\n"
|
|
"\n\n"
|
|
"if (defined($self->{lTimestamp}))\n"
|
|
"{\n"
|
|
"utime(time(), $self->{lTimestamp}, $strCurrentName)\n"
|
|
"or logErrorResult(ERROR_FILE_WRITE, \"unable to set time for '${strCurrentName}'\", $OS_ERROR);\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if ($self->{bAtomic})\n"
|
|
"{\n"
|
|
"$self->{oDriver}->move($strCurrentName, $self->{strName});\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"$self->resultSet(COMMON_IO_HANDLE, $self->{lSize});\n"
|
|
"\n\n"
|
|
"$self->SUPER::close();\n"
|
|
"}\n"
|
|
"\n"
|
|
"return true;\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub DESTROY\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n"
|
|
"if (defined($self->handle()))\n"
|
|
"{\n"
|
|
"CORE::close($self->handle());\n"
|
|
"undef($self->{fhFile});\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub handle {shift->{fhFile}}\n"
|
|
"sub opened {shift->{bOpened}}\n"
|
|
"sub name {shift->{strName}}\n"
|
|
"\n"
|
|
"1;\n"
|
|
},
|
|
{
|
|
.name = "pgBackRest/Storage/S3/Auth.pm",
|
|
.data =
|
|
"\n\n\n\n\n\n"
|
|
"package pgBackRest::Storage::S3::Auth;\n"
|
|
"\n"
|
|
"use strict;\n"
|
|
"use warnings FATAL => qw(all);\n"
|
|
"use Carp qw(confess);\n"
|
|
"use English '-no_match_vars';\n"
|
|
"\n"
|
|
"use Digest::SHA qw(hmac_sha256 hmac_sha256_hex);\n"
|
|
"use Exporter qw(import);\n"
|
|
"our @EXPORT = qw();\n"
|
|
"use POSIX qw(strftime);\n"
|
|
"\n"
|
|
"use pgBackRest::Common::Http::Common;\n"
|
|
"use pgBackRest::Common::Log;\n"
|
|
"use pgBackRest::LibC qw(:crypto);\n"
|
|
"\n\n\n\n"
|
|
"use constant S3 => 's3';\n"
|
|
"use constant AWS4 => 'AWS4';\n"
|
|
"use constant AWS4_REQUEST => 'aws4_request';\n"
|
|
"use constant AWS4_HMAC_SHA256 => 'AWS4-HMAC-SHA256';\n"
|
|
"\n"
|
|
"use constant S3_HEADER_AUTHORIZATION => 'authorization';\n"
|
|
"push @EXPORT, qw(S3_HEADER_AUTHORIZATION);\n"
|
|
"use constant S3_HEADER_DATE => 'x-amz-date';\n"
|
|
"push @EXPORT, qw(S3_HEADER_DATE);\n"
|
|
"use constant S3_HEADER_CONTENT_SHA256 => 'x-amz-content-sha256';\n"
|
|
"push @EXPORT, qw(S3_HEADER_CONTENT_SHA256);\n"
|
|
"use constant S3_HEADER_HOST => 'host';\n"
|
|
"push @EXPORT, qw(S3_HEADER_HOST);\n"
|
|
"use constant S3_HEADER_TOKEN => 'x-amz-security-token';\n"
|
|
"push @EXPORT, qw(S3_HEADER_TOKEN);\n"
|
|
"\n"
|
|
"use constant PAYLOAD_DEFAULT_HASH => cryptoHashOne('sha256', '');\n"
|
|
"push @EXPORT, qw(PAYLOAD_DEFAULT_HASH);\n"
|
|
"\n\n\n\n"
|
|
"sub s3DateTime\n"
|
|
"{\n"
|
|
"\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$lTime,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '::s3DateTime', \\@_,\n"
|
|
"{name => 'lTime', default => time(), trace => true},\n"
|
|
");\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'strDateTime', value => strftime(\"%Y%m%dT%H%M%SZ\", gmtime($lTime)), trace => true}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n"
|
|
"push @EXPORT, qw(s3DateTime);\n"
|
|
"\n\n\n\n"
|
|
"sub s3CanonicalRequest\n"
|
|
"{\n"
|
|
"\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$strVerb,\n"
|
|
"$strUri,\n"
|
|
"$strQuery,\n"
|
|
"$hHeader,\n"
|
|
"$strPayloadHash,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '::s3CanonicalRequest', \\@_,\n"
|
|
"{name => 'strVerb', trace => true},\n"
|
|
"{name => 'strUri', trace => true},\n"
|
|
"{name => 'strQuery', trace => true},\n"
|
|
"{name => 'hHeader', trace => true},\n"
|
|
"{name => 'strPayloadHash', trace => true},\n"
|
|
");\n"
|
|
"\n\n"
|
|
"my $strCanonicalRequest =\n"
|
|
"\"${strVerb}\\n${strUri}\\n${strQuery}\\n\";\n"
|
|
"my $strSignedHeaders;\n"
|
|
"\n"
|
|
"foreach my $strHeader (sort(keys(%{$hHeader})))\n"
|
|
"{\n"
|
|
"if (lc($strHeader) ne $strHeader)\n"
|
|
"{\n"
|
|
"confess &log(ASSERT, \"header '${strHeader}' must be lower case\");\n"
|
|
"}\n"
|
|
"\n"
|
|
"$strCanonicalRequest .= $strHeader . \":$hHeader->{$strHeader}\\n\";\n"
|
|
"$strSignedHeaders .= (defined($strSignedHeaders) ? qw(;) : '') . lc($strHeader);\n"
|
|
"}\n"
|
|
"\n"
|
|
"$strCanonicalRequest .= \"\\n${strSignedHeaders}\\n${strPayloadHash}\";\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'strCanonicalRequest', value => $strCanonicalRequest, trace => true},\n"
|
|
"{name => 'strSignedHeaders', value => $strSignedHeaders, trace => true},\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n"
|
|
"push @EXPORT, qw(s3CanonicalRequest);\n"
|
|
"\n\n\n\n"
|
|
"my $hSigningKeyCache;\n"
|
|
"\n"
|
|
"sub s3SigningKey\n"
|
|
"{\n"
|
|
"\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$strDate,\n"
|
|
"$strRegion,\n"
|
|
"$strSecretAccessKey,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '::s3SigningKey', \\@_,\n"
|
|
"{name => 'strDate', trace => true},\n"
|
|
"{name => 'strRegion', trace => true},\n"
|
|
"{name => 'strSecretAccessKey', redact => true, trace => true},\n"
|
|
");\n"
|
|
"\n\n"
|
|
"my $strSigningKey = $hSigningKeyCache->{$strDate}{$strRegion}{$strSecretAccessKey};\n"
|
|
"\n\n"
|
|
"if (!defined($strSigningKey))\n"
|
|
"{\n"
|
|
"my $strDateKey = hmac_sha256($strDate, AWS4 . $strSecretAccessKey);\n"
|
|
"my $strRegionKey = hmac_sha256($strRegion, $strDateKey);\n"
|
|
"my $strServiceKey = hmac_sha256(S3, $strRegionKey);\n"
|
|
"$strSigningKey = hmac_sha256(AWS4_REQUEST, $strServiceKey);\n"
|
|
"\n\n"
|
|
"$hSigningKeyCache->{$strDate}{$strRegion}{$strSecretAccessKey} = $strSigningKey;\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'strSigningKey', value => $strSigningKey, redact => true, trace => true}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n"
|
|
"push @EXPORT, qw(s3SigningKey);\n"
|
|
"\n\n\n\n"
|
|
"sub s3StringToSign\n"
|
|
"{\n"
|
|
"\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$strDateTime,\n"
|
|
"$strRegion,\n"
|
|
"$strCanonicalRequestHash,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '::s3StringToSign', \\@_,\n"
|
|
"{name => 'strDateTime', trace => true},\n"
|
|
"{name => 'strRegion', trace => true},\n"
|
|
"{name => 'strCanonicalRequestHash', trace => true},\n"
|
|
");\n"
|
|
"\n"
|
|
"my $strStringToSign =\n"
|
|
"AWS4_HMAC_SHA256 . \"\\n${strDateTime}\\n\" . substr($strDateTime, 0, 8) . \"/${strRegion}/\" . S3 . '/' . AWS4_REQUEST . \"\\n\" .\n"
|
|
"$strCanonicalRequestHash;\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'strStringToSign', value => $strStringToSign, trace => true}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n"
|
|
"push @EXPORT, qw(s3StringToSign);\n"
|
|
"\n\n\n\n"
|
|
"sub s3AuthorizationHeader\n"
|
|
"{\n"
|
|
"\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$strRegion,\n"
|
|
"$strHost,\n"
|
|
"$strVerb,\n"
|
|
"$strUri,\n"
|
|
"$strQuery,\n"
|
|
"$strDateTime,\n"
|
|
"$hHeader,\n"
|
|
"$strAccessKeyId,\n"
|
|
"$strSecretAccessKey,\n"
|
|
"$strSecurityToken,\n"
|
|
"$strPayloadHash,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '::s3AuthorizationHeader', \\@_,\n"
|
|
"{name => 'strRegion', trace => true},\n"
|
|
"{name => 'strHost', trace => true},\n"
|
|
"{name => 'strVerb', trace => true},\n"
|
|
"{name => 'strUri', trace => true},\n"
|
|
"{name => 'strQuery', trace => true},\n"
|
|
"{name => 'strDateTime', trace => true},\n"
|
|
"{name => 'hHeader', required => false, trace => true},\n"
|
|
"{name => 'strAccessKeyId', redact => true, trace => true},\n"
|
|
"{name => 'strSecretAccessKey', redact => true, trace => true},\n"
|
|
"{name => 'strSecurityToken', required => false, redact => true, trace => true},\n"
|
|
"{name => 'strPayloadHash', trace => true},\n"
|
|
");\n"
|
|
"\n\n"
|
|
"delete($hHeader->{&S3_HEADER_AUTHORIZATION});\n"
|
|
"\n\n"
|
|
"$hHeader->{&S3_HEADER_HOST} = $strHost;\n"
|
|
"$hHeader->{&S3_HEADER_CONTENT_SHA256} = $strPayloadHash;\n"
|
|
"$hHeader->{&S3_HEADER_DATE} = $strDateTime;\n"
|
|
"\n\n"
|
|
"if (defined($strSecurityToken))\n"
|
|
"{\n"
|
|
"$hHeader->{&S3_HEADER_TOKEN} = $strSecurityToken;\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"my ($strCanonicalRequest, $strSignedHeaders) = s3CanonicalRequest(\n"
|
|
"$strVerb, httpUriEncode($strUri, true), $strQuery, $hHeader, $strPayloadHash);\n"
|
|
"my $strStringToSign = s3StringToSign($strDateTime, $strRegion, cryptoHashOne('sha256', $strCanonicalRequest));\n"
|
|
"\n"
|
|
"$hHeader->{&S3_HEADER_AUTHORIZATION} =\n"
|
|
"AWS4_HMAC_SHA256 . \" Credential=${strAccessKeyId}/\" . substr($strDateTime, 0, 8) . \"/${strRegion}/\" . S3 . qw(/) .\n"
|
|
"AWS4_REQUEST . \",SignedHeaders=${strSignedHeaders},Signature=\" . hmac_sha256_hex($strStringToSign,\n"
|
|
"s3SigningKey(substr($strDateTime, 0, 8), $strRegion, $strSecretAccessKey));\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'hHeader', value => $hHeader, trace => true},\n"
|
|
"{name => 'strCanonicalRequest', value => $strCanonicalRequest, trace => true},\n"
|
|
"{name => 'strSignedHeaders', value => $strSignedHeaders, trace => true},\n"
|
|
"{name => 'strStringToSign', value => $strStringToSign, trace => true},\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n"
|
|
"push @EXPORT, qw(s3AuthorizationHeader);\n"
|
|
"\n"
|
|
"1;\n"
|
|
},
|
|
{
|
|
.name = "pgBackRest/Storage/S3/Driver.pm",
|
|
.data =
|
|
"\n\n\n"
|
|
"package pgBackRest::Storage::S3::Driver;\n"
|
|
"use parent 'pgBackRest::Storage::S3::Request';\n"
|
|
"\n"
|
|
"use strict;\n"
|
|
"use warnings FATAL => qw(all);\n"
|
|
"use Carp qw(confess);\n"
|
|
"use English '-no_match_vars';\n"
|
|
"\n"
|
|
"use Exporter qw(import);\n"
|
|
"our @EXPORT = qw();\n"
|
|
"use Digest::MD5 qw(md5_base64);\n"
|
|
"use File::Basename qw(basename dirname);\n"
|
|
"\n"
|
|
"use pgBackRest::Common::Exception;\n"
|
|
"use pgBackRest::Common::Log;\n"
|
|
"use pgBackRest::Common::String;\n"
|
|
"use pgBackRest::Common::Xml;\n"
|
|
"use pgBackRest::Storage::S3::FileRead;\n"
|
|
"use pgBackRest::Storage::S3::FileWrite;\n"
|
|
"use pgBackRest::Storage::S3::Request;\n"
|
|
"use pgBackRest::Storage::S3::Info;\n"
|
|
"\n\n\n\n"
|
|
"use constant STORAGE_S3_DRIVER => __PACKAGE__;\n"
|
|
"push @EXPORT, qw(STORAGE_S3_DRIVER);\n"
|
|
"\n\n\n\n"
|
|
"use constant S3_QUERY_CONTINUATION_TOKEN => 'continuation-token';\n"
|
|
"use constant S3_QUERY_DELIMITER => 'delimiter';\n"
|
|
"use constant S3_QUERY_LIST_TYPE => 'list-type';\n"
|
|
"use constant S3_QUERY_PREFIX => 'prefix';\n"
|
|
"\n\n\n\n"
|
|
"use constant S3_BATCH_MAX => 1000;\n"
|
|
"\n\n\n\n"
|
|
"sub openWrite\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$strFile,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->openWrite', \\@_,\n"
|
|
"{name => 'strFile', trace => true},\n"
|
|
");\n"
|
|
"\n"
|
|
"my $oFileIO = new pgBackRest::Storage::S3::FileWrite($self, $strFile);\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'oFileIO', value => $oFileIO, trace => true},\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub openRead\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$strFile,\n"
|
|
"$bIgnoreMissing,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->openRead', \\@_,\n"
|
|
"{name => 'strFile', trace => true},\n"
|
|
"{name => 'bIgnoreMissing', optional => true, default => false, trace => true},\n"
|
|
");\n"
|
|
"\n"
|
|
"my $oFileIO = new pgBackRest::Storage::S3::FileRead($self, $strFile, {bIgnoreMissing => $bIgnoreMissing});\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'oFileIO', value => $oFileIO, trace => true},\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub manifest\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$strPath,\n"
|
|
"$bRecurse,\n"
|
|
"$bPath,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->manifest', \\@_,\n"
|
|
"{name => 'strPath', trace => true},\n"
|
|
"\n"
|
|
"{name => 'bRecurse', optional => true, default => true, trace => true},\n"
|
|
"{name => 'bPath', optional => true, default => true, trace => true},\n"
|
|
");\n"
|
|
"\n\n"
|
|
"my $strPrefix = $strPath eq qw{/} ? undef : substr($strPath, 1) . ($bPath ? qw{/} : '');\n"
|
|
"\n\n"
|
|
"my $strDelimiter = $bRecurse ? undef : '/';\n"
|
|
"\n\n"
|
|
"my $hManifest = {};\n"
|
|
"\n\n"
|
|
"my $strContinuationToken;\n"
|
|
"\n"
|
|
"do\n"
|
|
"{\n"
|
|
"\n"
|
|
"my $oResponse = $self->request(\n"
|
|
"HTTP_VERB_GET, {hQuery =>\n"
|
|
"{&S3_QUERY_LIST_TYPE => 2, &S3_QUERY_PREFIX => $strPrefix, &S3_QUERY_DELIMITER => $strDelimiter,\n"
|
|
"&S3_QUERY_CONTINUATION_TOKEN => $strContinuationToken}, strResponseType => S3_RESPONSE_TYPE_XML});\n"
|
|
"\n\n"
|
|
"if (defined($strPrefix) && !$bPath)\n"
|
|
"{\n"
|
|
"\n"
|
|
"if (index($strPrefix, qw{/}) == -1)\n"
|
|
"{\n"
|
|
"undef($strPrefix);\n"
|
|
"}\n"
|
|
"else\n"
|
|
"{\n"
|
|
"$strPrefix = dirname($strPrefix) . qw{/};\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"foreach my $oFile (xmlTagChildren($oResponse, \"Contents\"))\n"
|
|
"{\n"
|
|
"my $strName = xmlTagText($oFile, \"Key\");\n"
|
|
"\n\n"
|
|
"if (defined($strPrefix))\n"
|
|
"{\n"
|
|
"$strName = substr($strName, length($strPrefix));\n"
|
|
"}\n"
|
|
"\n"
|
|
"$hManifest->{$strName}->{type} = 'f';\n"
|
|
"$hManifest->{$strName}->{size} = xmlTagText($oFile, 'Size') + 0;\n"
|
|
"\n\n"
|
|
"if ($bRecurse)\n"
|
|
"{\n"
|
|
"my @stryName = split(qw{/}, $strName);\n"
|
|
"\n"
|
|
"if (@stryName > 1)\n"
|
|
"{\n"
|
|
"$strName = undef;\n"
|
|
"\n"
|
|
"for (my $iIndex = 0; $iIndex < @stryName - 1; $iIndex++)\n"
|
|
"{\n"
|
|
"$strName .= (defined($strName) ? qw{/} : '') . $stryName[$iIndex];\n"
|
|
"$hManifest->{$strName}->{type} = 'd';\n"
|
|
"}\n"
|
|
"}\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if ($bPath && !$bRecurse)\n"
|
|
"{\n"
|
|
"foreach my $oPath (xmlTagChildren($oResponse, \"CommonPrefixes\"))\n"
|
|
"{\n"
|
|
"my $strName = xmlTagText($oPath, \"Prefix\");\n"
|
|
"\n\n"
|
|
"if (defined($strPrefix))\n"
|
|
"{\n"
|
|
"$strName = substr($strName, length($strPrefix));\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"$strName = substr($strName, 0, length($strName) - 1);\n"
|
|
"\n"
|
|
"$hManifest->{$strName}->{type} = 'd';\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n"
|
|
"$strContinuationToken = xmlTagText($oResponse, \"NextContinuationToken\", false);\n"
|
|
"}\n"
|
|
"while (defined($strContinuationToken));\n"
|
|
"\n\n"
|
|
"if ($bPath)\n"
|
|
"{\n"
|
|
"$hManifest->{qw{.}}->{type} = 'd';\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'hManifest', value => $hManifest, trace => true}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub list\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$strPath,\n"
|
|
"$strExpression\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->list', \\@_,\n"
|
|
"{name => 'strPath', trace => true},\n"
|
|
"{name => 'strExpression', optional => true, trace => true},\n"
|
|
");\n"
|
|
"\n\n"
|
|
"my $strPrefix = regexPrefix($strExpression);\n"
|
|
"\n\n"
|
|
"my @stryFileList = grep(\n"
|
|
"!/^\\.$/i, keys(%{$self->manifest(\n"
|
|
"$strPath . (defined($strPrefix) ? \"/${strPrefix}\" : ''), {bRecurse => false, bPath => !defined($strPrefix)})}));\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'stryFileList', value => \\@stryFileList, ref => true, trace => true}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub pathCreate\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$strPath,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->pathCreate', \\@_,\n"
|
|
"{name => 'strPath', trace => true},\n"
|
|
");\n"
|
|
"\n\n"
|
|
"return logDebugReturn($strOperation);\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub pathSync\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$strPath,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->pathSync', \\@_,\n"
|
|
"{name => 'strPath', trace => true},\n"
|
|
");\n"
|
|
"\n\n"
|
|
"return logDebugReturn($strOperation);\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub exists\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$strFile,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->exists', \\@_,\n"
|
|
"{name => 'strFile', trace => true},\n"
|
|
");\n"
|
|
"\n\n"
|
|
"my $bExists = defined($self->manifest($strFile, {bRecurse => false, bPath => false})->{basename($strFile)}) ? true : false;\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'bExists', value => $bExists, trace => true}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub pathExists\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$strPath,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->pathExists', \\@_,\n"
|
|
"{name => 'strPath', trace => true},\n"
|
|
");\n"
|
|
"\n"
|
|
"my $bExists = true;\n"
|
|
"\n\n"
|
|
"if ($strPath ne qw{/})\n"
|
|
"{\n"
|
|
"\n"
|
|
"my $rhInfo = $self->manifest(dirname($strPath), {bRecurse => false, bPath => true})->{basename($strPath)};\n"
|
|
"$bExists = defined($rhInfo) && $rhInfo->{type} eq 'd' ? true : false;\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'bExists', value => $bExists, trace => true}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub info\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$strFile,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->info', \\@_,\n"
|
|
"{name => 'strFile', trace => true},\n"
|
|
");\n"
|
|
"\n\n"
|
|
"my $rhFile = $self->manifest($strFile, {bRecurse => false, bPath => false})->{basename($strFile)};\n"
|
|
"\n"
|
|
"if (!defined($rhFile))\n"
|
|
"{\n"
|
|
"confess &log(ERROR, \"unable to get info for missing file ${strFile}\", ERROR_FILE_MISSING);\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'oInfo', value => new pgBackRest::Storage::S3::Info($rhFile->{size}), trace => true}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub remove\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$rstryFile,\n"
|
|
"$bRecurse,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->remove', \\@_,\n"
|
|
"{name => 'rstryFile', trace => true},\n"
|
|
"{name => 'bRecurse', optional => true, default => false, trace => true},\n"
|
|
");\n"
|
|
"\n\n"
|
|
"if ($bRecurse)\n"
|
|
"{\n"
|
|
"my $rhManifest = $self->manifest($rstryFile);\n"
|
|
"my @stryRemoveFile;\n"
|
|
"\n\n"
|
|
"foreach my $strFile (sort({$b cmp $a} keys(%{$rhManifest})))\n"
|
|
"{\n"
|
|
"next if $rhManifest->{$strFile}->{type} eq 'd';\n"
|
|
"push(@stryRemoveFile, \"${rstryFile}/${strFile}\");\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if (@stryRemoveFile > 0)\n"
|
|
"{\n"
|
|
"$self->remove(\\@stryRemoveFile);\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n"
|
|
"else\n"
|
|
"{\n"
|
|
"\n"
|
|
"my $rstryFileAll = ref($rstryFile) ? $rstryFile : [$rstryFile];\n"
|
|
"\n"
|
|
"do\n"
|
|
"{\n"
|
|
"my $strFile = shift(@{$rstryFileAll});\n"
|
|
"my $iTotal = 0;\n"
|
|
"my $strXml = XML_HEADER . '<Delete><Quiet>true</Quiet>';\n"
|
|
"\n"
|
|
"while (defined($strFile))\n"
|
|
"{\n"
|
|
"$iTotal++;\n"
|
|
"$strXml .= '<Object><Key>' . xmlFromText(substr($strFile, 1)) . '</Key></Object>';\n"
|
|
"\n"
|
|
"$strFile = $iTotal < S3_BATCH_MAX ? shift(@{$rstryFileAll}) : undef;\n"
|
|
"}\n"
|
|
"\n"
|
|
"$strXml .= '</Delete>';\n"
|
|
"\n"
|
|
"my $hHeader = {'content-md5' => md5_base64($strXml) . '=='};\n"
|
|
"\n\n"
|
|
"my $oResponse = $self->request(\n"
|
|
"HTTP_VERB_POST,\n"
|
|
"{hQuery => 'delete=', rstrBody => \\$strXml, hHeader => $hHeader, strResponseType => S3_RESPONSE_TYPE_XML});\n"
|
|
"}\n"
|
|
"while (@{$rstryFileAll} > 0);\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'bResult', value => true, trace => true}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n\n"
|
|
"sub capability {false}\n"
|
|
"sub className {STORAGE_S3_DRIVER}\n"
|
|
"\n"
|
|
"1;\n"
|
|
},
|
|
{
|
|
.name = "pgBackRest/Storage/S3/FileRead.pm",
|
|
.data =
|
|
"\n\n\n"
|
|
"package pgBackRest::Storage::S3::FileRead;\n"
|
|
"use parent 'pgBackRest::Common::Http::Client';\n"
|
|
"\n"
|
|
"use strict;\n"
|
|
"use warnings FATAL => qw(all);\n"
|
|
"use Carp qw(confess);\n"
|
|
"use English '-no_match_vars';\n"
|
|
"\n"
|
|
"use Digest::MD5 qw(md5_base64);\n"
|
|
"use Fcntl qw(O_RDONLY O_WRONLY O_CREAT O_TRUNC);\n"
|
|
"use File::Basename qw(dirname);\n"
|
|
"\n"
|
|
"use pgBackRest::Common::Exception;\n"
|
|
"use pgBackRest::Common::Log;\n"
|
|
"use pgBackRest::Common::Xml;\n"
|
|
"use pgBackRest::Storage::Base;\n"
|
|
"use pgBackRest::Storage::S3::Request;\n"
|
|
"\n\n\n\n"
|
|
"sub new\n"
|
|
"{\n"
|
|
"my $class = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$oDriver,\n"
|
|
"$strName,\n"
|
|
"$bIgnoreMissing,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->new', \\@_,\n"
|
|
"{name => 'oDriver', trace => true},\n"
|
|
"{name => 'strName', trace => true},\n"
|
|
"{name => 'bIgnoreMissing', optional => true, default => false, trace => true},\n"
|
|
");\n"
|
|
"\n\n"
|
|
"my $self = $oDriver->request(\n"
|
|
"HTTP_VERB_GET, {strUri => $strName, strResponseType => S3_RESPONSE_TYPE_IO, bIgnoreMissing => $bIgnoreMissing});\n"
|
|
"\n\n"
|
|
"if (defined($self))\n"
|
|
"{\n"
|
|
"bless $self, $class;\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'self', value => $self, trace => true}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub name {shift->{strName}}\n"
|
|
"\n"
|
|
"1;\n"
|
|
},
|
|
{
|
|
.name = "pgBackRest/Storage/S3/FileWrite.pm",
|
|
.data =
|
|
"\n\n\n"
|
|
"package pgBackRest::Storage::S3::FileWrite;\n"
|
|
"use parent 'pgBackRest::Common::Io::Base';\n"
|
|
"\n"
|
|
"use strict;\n"
|
|
"use warnings FATAL => qw(all);\n"
|
|
"use Carp qw(confess);\n"
|
|
"use English '-no_match_vars';\n"
|
|
"\n"
|
|
"use Digest::MD5 qw(md5_base64);\n"
|
|
"use Fcntl qw(O_RDONLY O_WRONLY O_CREAT O_TRUNC);\n"
|
|
"use File::Basename qw(dirname);\n"
|
|
"\n"
|
|
"use pgBackRest::Common::Exception;\n"
|
|
"use pgBackRest::Common::Io::Handle;\n"
|
|
"use pgBackRest::Common::Log;\n"
|
|
"use pgBackRest::Common::Xml;\n"
|
|
"use pgBackRest::Storage::Base;\n"
|
|
"use pgBackRest::Storage::S3::Request;\n"
|
|
"\n\n\n\n"
|
|
"use constant S3_BUFFER_MAX => 16777216;\n"
|
|
"\n\n\n\n"
|
|
"sub new\n"
|
|
"{\n"
|
|
"my $class = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$oDriver,\n"
|
|
"$strName,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->new', \\@_,\n"
|
|
"{name => 'oDriver', trace => true},\n"
|
|
"{name => 'strName', trace => true},\n"
|
|
");\n"
|
|
"\n\n"
|
|
"my $self = $class->SUPER::new(\"'${strName}'\");\n"
|
|
"bless $self, $class;\n"
|
|
"\n\n"
|
|
"$self->{oDriver} = $oDriver;\n"
|
|
"$self->{strName} = $strName;\n"
|
|
"\n\n"
|
|
"$self->{rtBuffer} = '';\n"
|
|
"\n\n"
|
|
"$self->{bWritten} = false;\n"
|
|
"\n\n"
|
|
"$self->{lSize} = 0;\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'self', value => $self, trace => true}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub open\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my $oResponse = $self->{oDriver}->request(\n"
|
|
"HTTP_VERB_POST, {strUri => $self->{strName}, hQuery => 'uploads=', strResponseType => S3_RESPONSE_TYPE_XML});\n"
|
|
"\n"
|
|
"$self->{strUploadId} = xmlTagText($oResponse, 'UploadId');\n"
|
|
"\n\n"
|
|
"$self->{rstryMultiPart} = [];\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub write\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"my $rtBuffer = shift;\n"
|
|
"\n\n"
|
|
"$self->{bWritten} = true;\n"
|
|
"\n"
|
|
"if (defined($rtBuffer))\n"
|
|
"{\n"
|
|
"$self->{rtBuffer} .= $$rtBuffer;\n"
|
|
"$self->{lSize} += length($$rtBuffer);\n"
|
|
"\n\n"
|
|
"if (length($self->{rtBuffer}) >= S3_BUFFER_MAX)\n"
|
|
"{\n"
|
|
"$self->flush();\n"
|
|
"}\n"
|
|
"\n"
|
|
"return length($$rtBuffer);\n"
|
|
"}\n"
|
|
"\n"
|
|
"return 0;\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub flush\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"$self->open() if !$self->opened();\n"
|
|
"\n\n"
|
|
"$self->{oDriver}->request(\n"
|
|
"HTTP_VERB_PUT,\n"
|
|
"{strUri => $self->{strName},\n"
|
|
"hQuery => {'partNumber' => @{$self->{rstryMultiPart}} + 1, 'uploadId' => $self->{strUploadId}},\n"
|
|
"rstrBody => \\$self->{rtBuffer}, hHeader => {'content-md5' => md5_base64($self->{rtBuffer}) . '=='}});\n"
|
|
"\n\n"
|
|
"push(@{$self->{rstryMultiPart}}, $self->{oDriver}->{hResponseHeader}{&S3_HEADER_ETAG});\n"
|
|
"\n\n"
|
|
"$self->{rtBuffer} = '';\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub close\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"if ($self->{bWritten})\n"
|
|
"{\n"
|
|
"\n"
|
|
"$self->{bWritten} = false;\n"
|
|
"\n\n"
|
|
"if ($self->opened())\n"
|
|
"{\n"
|
|
"\n"
|
|
"$self->flush();\n"
|
|
"\n"
|
|
"my $strXml = XML_HEADER . '<CompleteMultipartUpload>';\n"
|
|
"my $iPartNo = 0;\n"
|
|
"\n"
|
|
"foreach my $strETag (@{$self->{rstryMultiPart}})\n"
|
|
"{\n"
|
|
"$iPartNo++;\n"
|
|
"\n"
|
|
"$strXml .= \"<Part><PartNumber>${iPartNo}</PartNumber><ETag>${strETag}</ETag></Part>\";\n"
|
|
"}\n"
|
|
"\n"
|
|
"$strXml .= '</CompleteMultipartUpload>';\n"
|
|
"\n\n"
|
|
"my $oResponse = $self->{oDriver}->request(\n"
|
|
"HTTP_VERB_POST,\n"
|
|
"{strUri => $self->{strName}, hQuery => {'uploadId' => $self->{strUploadId}},\n"
|
|
"rstrBody => \\$strXml, hHeader => {'content-md5' => md5_base64($strXml) . '=='},\n"
|
|
"strResponseType => S3_RESPONSE_TYPE_XML});\n"
|
|
"}\n"
|
|
"\n"
|
|
"else\n"
|
|
"{\n"
|
|
"$self->{oDriver}->request(\n"
|
|
"HTTP_VERB_PUT,\n"
|
|
"{strUri => $self->{strName}, rstrBody => \\$self->{rtBuffer},\n"
|
|
"hHeader => {'content-md5' => md5_base64($self->{rtBuffer}) . '=='}});\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"$self->resultSet(COMMON_IO_HANDLE, $self->{lSize});\n"
|
|
"\n"
|
|
"return true;\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub opened {defined(shift->{strUploadId})}\n"
|
|
"sub name {shift->{strName}}\n"
|
|
"\n"
|
|
"1;\n"
|
|
},
|
|
{
|
|
.name = "pgBackRest/Storage/S3/Info.pm",
|
|
.data =
|
|
"\n\n\n"
|
|
"package pgBackRest::Storage::S3::Info;\n"
|
|
"\n"
|
|
"use strict;\n"
|
|
"use warnings FATAL => qw(all);\n"
|
|
"use Carp qw(confess);\n"
|
|
"use English '-no_match_vars';\n"
|
|
"\n"
|
|
"use pgBackRest::Common::Log;\n"
|
|
"\n\n\n\n"
|
|
"sub new\n"
|
|
"{\n"
|
|
"my $class = shift;\n"
|
|
"\n\n"
|
|
"my $self = {};\n"
|
|
"bless $self, $class;\n"
|
|
"\n\n"
|
|
"(\n"
|
|
"my $strOperation,\n"
|
|
"$self->{lSize},\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->new', \\@_,\n"
|
|
"{name => 'lSize'},\n"
|
|
");\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'self', value => $self}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub size {shift->{lSize}}\n"
|
|
"\n"
|
|
"1;\n"
|
|
},
|
|
{
|
|
.name = "pgBackRest/Storage/S3/Request.pm",
|
|
.data =
|
|
"\n\n\n"
|
|
"package pgBackRest::Storage::S3::Request;\n"
|
|
"\n"
|
|
"use strict;\n"
|
|
"use warnings FATAL => qw(all);\n"
|
|
"use Carp qw(confess);\n"
|
|
"use English '-no_match_vars';\n"
|
|
"\n"
|
|
"use Exporter qw(import);\n"
|
|
"our @EXPORT = qw();\n"
|
|
"use IO::Socket::SSL;\n"
|
|
"\n"
|
|
"use pgBackRest::Common::Exception;\n"
|
|
"use pgBackRest::Common::Http::Client;\n"
|
|
"use pgBackRest::Common::Http::Common;\n"
|
|
"use pgBackRest::Common::Io::Base;\n"
|
|
"use pgBackRest::Common::Log;\n"
|
|
"use pgBackRest::Common::String;\n"
|
|
"use pgBackRest::Common::Xml;\n"
|
|
"use pgBackRest::LibC qw(:crypto);\n"
|
|
"use pgBackRest::Storage::S3::Auth;\n"
|
|
"\n\n\n\n"
|
|
"use constant HTTP_VERB_GET => 'GET';\n"
|
|
"push @EXPORT, qw(HTTP_VERB_GET);\n"
|
|
"use constant HTTP_VERB_POST => 'POST';\n"
|
|
"push @EXPORT, qw(HTTP_VERB_POST);\n"
|
|
"use constant HTTP_VERB_PUT => 'PUT';\n"
|
|
"push @EXPORT, qw(HTTP_VERB_PUT);\n"
|
|
"\n"
|
|
"use constant S3_HEADER_CONTENT_LENGTH => 'content-length';\n"
|
|
"push @EXPORT, qw(S3_HEADER_CONTENT_LENGTH);\n"
|
|
"use constant S3_HEADER_TRANSFER_ENCODING => 'transfer-encoding';\n"
|
|
"push @EXPORT, qw(S3_HEADER_TRANSFER_ENCODING);\n"
|
|
"use constant S3_HEADER_ETAG => 'etag';\n"
|
|
"push @EXPORT, qw(S3_HEADER_ETAG);\n"
|
|
"\n"
|
|
"use constant S3_RESPONSE_TYPE_IO => 'io';\n"
|
|
"push @EXPORT, qw(S3_RESPONSE_TYPE_IO);\n"
|
|
"use constant S3_RESPONSE_TYPE_NONE => 'none';\n"
|
|
"push @EXPORT, qw(S3_RESPONSE_TYPE_NONE);\n"
|
|
"use constant S3_RESPONSE_TYPE_XML => 'xml';\n"
|
|
"push @EXPORT, qw(S3_RESPONSE_TYPE_XML);\n"
|
|
"\n"
|
|
"use constant S3_RESPONSE_CODE_SUCCESS => 200;\n"
|
|
"use constant S3_RESPONSE_CODE_ERROR_AUTH => 403;\n"
|
|
"use constant S3_RESPONSE_CODE_ERROR_NOT_FOUND => 404;\n"
|
|
"use constant S3_RESPONSE_CODE_ERROR_RETRY_CLASS => 5;\n"
|
|
"\n"
|
|
"use constant S3_RETRY_MAX => 4;\n"
|
|
"\n\n\n\n"
|
|
"sub new\n"
|
|
"{\n"
|
|
"my $class = shift;\n"
|
|
"\n\n"
|
|
"my $self = {};\n"
|
|
"bless $self, $class;\n"
|
|
"\n\n"
|
|
"(\n"
|
|
"my $strOperation,\n"
|
|
"$self->{strBucket},\n"
|
|
"$self->{strEndPoint},\n"
|
|
"$self->{strRegion},\n"
|
|
"$self->{strAccessKeyId},\n"
|
|
"$self->{strSecretAccessKey},\n"
|
|
"$self->{strSecurityToken},\n"
|
|
"$self->{strHost},\n"
|
|
"$self->{iPort},\n"
|
|
"$self->{bVerifySsl},\n"
|
|
"$self->{strCaPath},\n"
|
|
"$self->{strCaFile},\n"
|
|
"$self->{lBufferMax},\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->new', \\@_,\n"
|
|
"{name => 'strBucket'},\n"
|
|
"{name => 'strEndPoint'},\n"
|
|
"{name => 'strRegion'},\n"
|
|
"{name => 'strAccessKeyId', redact => true},\n"
|
|
"{name => 'strSecretAccessKey', redact => true},\n"
|
|
"{name => 'strSecurityToken', optional => true, redact => true},\n"
|
|
"{name => 'strHost', optional => true},\n"
|
|
"{name => 'iPort', optional => true},\n"
|
|
"{name => 'bVerifySsl', optional => true, default => true},\n"
|
|
"{name => 'strCaPath', optional => true},\n"
|
|
"{name => 'strCaFile', optional => true},\n"
|
|
"{name => 'lBufferMax', optional => true, default => COMMON_IO_BUFFER_MAX},\n"
|
|
");\n"
|
|
"\n\n"
|
|
"$self->{strHost} = defined($self->{strHost}) ? $self->{strHost} : \"$self->{strBucket}.$self->{strEndPoint}\";\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'self', value => $self, trace => true}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n\n\n\n"
|
|
"sub request\n"
|
|
"{\n"
|
|
"my $self = shift;\n"
|
|
"\n\n"
|
|
"my\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"$strVerb,\n"
|
|
"$strUri,\n"
|
|
"$hQuery,\n"
|
|
"$hHeader,\n"
|
|
"$rstrBody,\n"
|
|
"$strResponseType,\n"
|
|
"$bIgnoreMissing,\n"
|
|
") =\n"
|
|
"logDebugParam\n"
|
|
"(\n"
|
|
"__PACKAGE__ . '->request', \\@_,\n"
|
|
"{name => 'strVerb', trace => true},\n"
|
|
"{name => 'strUri', optional => true, default => '/', trace => true},\n"
|
|
"{name => 'hQuery', optional => true, trace => true},\n"
|
|
"{name => 'hHeader', optional => true, trace => true},\n"
|
|
"{name => 'rstrBody', optional => true, trace => true},\n"
|
|
"{name => 'strResponseType', optional => true, default => S3_RESPONSE_TYPE_NONE, trace => true},\n"
|
|
"{name => 'bIgnoreMissing', optional => true, default => false, trace => true},\n"
|
|
");\n"
|
|
"\n\n"
|
|
"my $oResponse;\n"
|
|
"\n\n"
|
|
"my $bRetry;\n"
|
|
"my $iRetryTotal = 0;\n"
|
|
"\n"
|
|
"do\n"
|
|
"{\n"
|
|
"\n"
|
|
"$bRetry = false;\n"
|
|
"\n\n"
|
|
"$hHeader->{&S3_HEADER_CONTENT_SHA256} = defined($rstrBody) ? cryptoHashOne('sha256', $$rstrBody) : PAYLOAD_DEFAULT_HASH;\n"
|
|
"$hHeader->{&S3_HEADER_CONTENT_LENGTH} = defined($rstrBody) ? length($$rstrBody) : 0;\n"
|
|
"\n\n"
|
|
"($hHeader, my $strCanonicalRequest, my $strSignedHeaders, my $strStringToSign) = s3AuthorizationHeader(\n"
|
|
"$self->{strRegion}, \"$self->{strBucket}.$self->{strEndPoint}\", $strVerb, $strUri, httpQuery($hQuery), s3DateTime(),\n"
|
|
"$hHeader, $self->{strAccessKeyId}, $self->{strSecretAccessKey}, $self->{strSecurityToken},\n"
|
|
"$hHeader->{&S3_HEADER_CONTENT_SHA256});\n"
|
|
"\n\n"
|
|
"my $oHttpClient = new pgBackRest::Common::Http::Client(\n"
|
|
"$self->{strHost}, $strVerb,\n"
|
|
"{iPort => $self->{iPort}, strUri => $strUri, hQuery => $hQuery, hRequestHeader => $hHeader,\n"
|
|
"rstrRequestBody => $rstrBody, bVerifySsl => $self->{bVerifySsl}, strCaPath => $self->{strCaPath},\n"
|
|
"strCaFile => $self->{strCaFile}, bResponseBodyPrefetch => $strResponseType eq S3_RESPONSE_TYPE_XML,\n"
|
|
"lBufferMax => $self->{lBufferMax}});\n"
|
|
"\n\n"
|
|
"my $iResponseCode = $oHttpClient->responseCode();\n"
|
|
"\n"
|
|
"if ($iResponseCode == S3_RESPONSE_CODE_SUCCESS)\n"
|
|
"{\n"
|
|
"\n"
|
|
"$self->{hResponseHeader} = $oHttpClient->responseHeader();\n"
|
|
"\n\n"
|
|
"if ($strResponseType eq S3_RESPONSE_TYPE_XML)\n"
|
|
"{\n"
|
|
"my $rtResponseBody = $oHttpClient->responseBody();\n"
|
|
"\n"
|
|
"if ($oHttpClient->contentLength() == 0 || !defined($$rtResponseBody))\n"
|
|
"{\n"
|
|
"confess &log(ERROR,\n"
|
|
"\"response type '${strResponseType}' was requested but content length is zero or content is missing\",\n"
|
|
"ERROR_PROTOCOL);\n"
|
|
"}\n"
|
|
"\n"
|
|
"$oResponse = xmlParse($$rtResponseBody);\n"
|
|
"}\n"
|
|
"\n"
|
|
"elsif ($strResponseType eq S3_RESPONSE_TYPE_IO)\n"
|
|
"{\n"
|
|
"$oResponse = $oHttpClient;\n"
|
|
"}\n"
|
|
"}\n"
|
|
"else\n"
|
|
"{\n"
|
|
"\n"
|
|
"if ($iResponseCode == S3_RESPONSE_CODE_ERROR_NOT_FOUND)\n"
|
|
"{\n"
|
|
"\n"
|
|
"if (!$bIgnoreMissing)\n"
|
|
"{\n"
|
|
"confess &log(ERROR, \"unable to open '${strUri}': No such file or directory\", ERROR_FILE_MISSING);\n"
|
|
"}\n"
|
|
"\n"
|
|
"$bRetry = false;\n"
|
|
"}\n"
|
|
"\n"
|
|
"else\n"
|
|
"{\n"
|
|
"\n"
|
|
"if (int($iResponseCode / 100) == S3_RESPONSE_CODE_ERROR_RETRY_CLASS)\n"
|
|
"{\n"
|
|
"\n"
|
|
"$iRetryTotal++;\n"
|
|
"$bRetry = $iRetryTotal <= S3_RETRY_MAX;\n"
|
|
"\n\n"
|
|
"if ($iRetryTotal > 1)\n"
|
|
"{\n"
|
|
"sleep(5);\n"
|
|
"}\n"
|
|
"}\n"
|
|
"\n\n"
|
|
"if (!$bRetry)\n"
|
|
"{\n"
|
|
"my $rstrResponseBody = $oHttpClient->responseBody();\n"
|
|
"\n\n"
|
|
"my $strRequestHeader = $oHttpClient->requestHeaderText();\n"
|
|
"$strRequestHeader =~ s/^${\\S3_HEADER_AUTHORIZATION}:.*$/${\\S3_HEADER_AUTHORIZATION}: <redacted>/mg;\n"
|
|
"\n"
|
|
"confess &log(ERROR,\n"
|
|
"'S3 request error' . ($iRetryTotal > 0 ? \" after \" . (S3_RETRY_MAX + 1) . \" tries\" : '') .\n"
|
|
"\" [$iResponseCode] \" . $oHttpClient->responseMessage() .\n"
|
|
"\"\\n*** request header ***\\n${strRequestHeader}\" .\n"
|
|
"($iResponseCode == S3_RESPONSE_CODE_ERROR_AUTH ?\n"
|
|
"\"\\n*** canonical request ***\\n\" . $strCanonicalRequest .\n"
|
|
"\"\\n*** signed headers ***\\n\" . $strSignedHeaders .\n"
|
|
"\"\\n*** string to sign ***\\n\" . $strStringToSign : '') .\n"
|
|
"\"\\n*** response header ***\\n\" . $oHttpClient->responseHeaderText() .\n"
|
|
"(defined($$rstrResponseBody) ? \"\\n*** response body ***\\n${$rstrResponseBody}\" : ''),\n"
|
|
"ERROR_PROTOCOL);\n"
|
|
"}\n"
|
|
"}\n"
|
|
"}\n"
|
|
"}\n"
|
|
"while ($bRetry);\n"
|
|
"\n\n"
|
|
"return logDebugReturn\n"
|
|
"(\n"
|
|
"$strOperation,\n"
|
|
"{name => 'oResponse', value => $oResponse, trace => true, ref => true}\n"
|
|
");\n"
|
|
"}\n"
|
|
"\n"
|
|
"1;\n"
|
|
},
|
|
{
|
|
.name = "pgBackRest/Version.pm",
|
|
.data =
|
|
"\n\n\n\n\n"
|
|
"package pgBackRest::Version;\n"
|
|
"\n"
|
|
"use strict;\n"
|
|
"use warnings FATAL => qw(all);\n"
|
|
"\n"
|
|
"use Cwd qw(abs_path);\n"
|
|
"use Exporter qw(import);\n"
|
|
"our @EXPORT = qw();\n"
|
|
"\n\n\n\n\n"
|
|
"use constant PROJECT_NAME => 'pgBackRest';\n"
|
|
"push @EXPORT, qw(PROJECT_NAME);\n"
|
|
"use constant PROJECT_EXE => lc(PROJECT_NAME);\n"
|
|
"push @EXPORT, qw(PROJECT_EXE);\n"
|
|
"use constant PROJECT_CONF => PROJECT_EXE . '.conf';\n"
|
|
"push @EXPORT, qw(PROJECT_CONF);\n"
|
|
"\n\n\n\n\n"
|
|
"my $strProjectBin;\n"
|
|
"\n"
|
|
"sub projectBin {return $strProjectBin};\n"
|
|
"sub projectBinSet {$strProjectBin = shift}\n"
|
|
"\n"
|
|
"push @EXPORT, qw(projectBin projectBinSet);\n"
|
|
"\n\n\n\n\n\n"
|
|
"use constant PROJECT_VERSION => '2.15dev';\n"
|
|
"push @EXPORT, qw(PROJECT_VERSION);\n"
|
|
"\n\n\n\n\n\n"
|
|
"use constant REPOSITORY_FORMAT => 5;\n"
|
|
"push @EXPORT, qw(REPOSITORY_FORMAT);\n"
|
|
"\n"
|
|
"1;\n"
|
|
},
|
|
};
|