1
0
mirror of https://github.com/pgbackrest/pgbackrest.git synced 2025-08-10 22:21:39 +02:00

The pgbackrest executable is now a C binary instead of Perl.

This allows certain time-critical commands (like async archive-push) to run more quickly.
This commit is contained in:
David Steele
2017-11-26 18:43:51 -05:00
parent 4d8ad4ac18
commit 74d6398ad2
23 changed files with 810 additions and 417 deletions

View File

@@ -1,293 +0,0 @@
#!/usr/bin/perl
####################################################################################################################################
# pgBackRest - Reliable PostgreSQL Backup & Restore
####################################################################################################################################
####################################################################################################################################
# Perl includes
####################################################################################################################################
use strict;
use warnings FATAL => qw(all);
use Carp qw(confess);
use English '-no_match_vars';
# Convert die to confess to capture the stack trace
$SIG{__DIE__} = sub { Carp::confess @_ };
use File::Basename qw(dirname);
use lib dirname($0) . '/../lib';
use pgBackRest::Common::Exception;
use pgBackRest::Common::Exit;
use pgBackRest::Common::Lock;
use pgBackRest::Common::Log;
use pgBackRest::Config::Config;
use pgBackRest::Protocol::Helper;
####################################################################################################################################
# Run in eval block to catch errors
####################################################################################################################################
eval
{
################################################################################################################################
# Load command line parameters and config
################################################################################################################################
my $bConfigResult = configLoad();
# Display help and version
if (cfgCommandTest(CFGCMD_HELP) || cfgCommandTest(CFGCMD_VERSION))
{
# Load module dynamically
require pgBackRest::Config::ConfigHelp;
pgBackRest::Config::ConfigHelp->import();
# Generate help and exit
configHelp($ARGV[1], $ARGV[2], cfgCommandTest(CFGCMD_VERSION), $bConfigResult);
exitSafe(0);
}
# Set test options
if (cfgOptionTest(CFGOPT_TEST) && cfgOption(CFGOPT_TEST))
{
testSet(cfgOption(CFGOPT_TEST), cfgOption(CFGOPT_TEST_DELAY), cfgOption(CFGOPT_TEST_POINT, false));
}
################################################################################################################################
# Process archive-push command
################################################################################################################################
if (cfgCommandTest(CFGCMD_ARCHIVE_PUSH))
{
# Load module dynamically
require pgBackRest::Archive::Push::Push;
pgBackRest::Archive::Push::Push->import();
exitSafe(new pgBackRest::Archive::Push::Push()->process($ARGV[1]));
}
################################################################################################################################
# Process archive-get command
################################################################################################################################
if (cfgCommandTest(CFGCMD_ARCHIVE_GET))
{
# Load module dynamically
require pgBackRest::Archive::Get::Get;
pgBackRest::Archive::Get::Get->import();
exitSafe(new pgBackRest::Archive::Get::Get()->process());
}
################################################################################################################################
# Process remote commands
################################################################################################################################
if (cfgCommandTest(CFGCMD_REMOTE))
{
# Set log levels
cfgOptionSet(CFGOPT_LOG_LEVEL_STDERR, PROTOCOL, true);
logLevelSet(OFF, OFF, cfgOption(CFGOPT_LOG_LEVEL_STDERR));
if (cfgOptionTest(CFGOPT_TYPE, CFGOPTVAL_REMOTE_TYPE_BACKUP) && !cfgOptionTest(CFGOPT_REPO_TYPE, CFGOPTVAL_REPO_TYPE_S3) &&
!-e cfgOption(CFGOPT_REPO_PATH))
{
confess &log(ERROR, 'repo-path \'' . cfgOption(CFGOPT_REPO_PATH) . '\' does not exist', ERROR_PATH_MISSING);
}
# Load module dynamically
require pgBackRest::Protocol::Remote::Minion;
pgBackRest::Protocol::Remote::Minion->import();
# Create the remote object
my $oRemote = new pgBackRest::Protocol::Remote::Minion(cfgOption(CFGOPT_BUFFER_SIZE), cfgOption(CFGOPT_PROTOCOL_TIMEOUT));
# Acquire a remote lock (except for commands that are read-only or local processes)
my $strLockName;
if (!(cfgOptionTest(CFGOPT_COMMAND, cfgCommandName(CFGCMD_ARCHIVE_GET)) ||
cfgOptionTest(CFGOPT_COMMAND, cfgCommandName(CFGCMD_INFO)) ||
cfgOptionTest(CFGOPT_COMMAND, cfgCommandName(CFGCMD_RESTORE)) ||
cfgOptionTest(CFGOPT_COMMAND, cfgCommandName(CFGCMD_CHECK)) ||
cfgOptionTest(CFGOPT_COMMAND, cfgCommandName(CFGCMD_LOCAL))))
{
$strLockName = cfgOption(CFGOPT_COMMAND);
}
# Process remote requests
exitSafe($oRemote->process($strLockName));
}
################################################################################################################################
# Process local commands
################################################################################################################################
if (cfgCommandTest(CFGCMD_LOCAL))
{
# Set log levels
cfgOptionSet(CFGOPT_LOG_LEVEL_STDERR, PROTOCOL, true);
logLevelSet(OFF, OFF, cfgOption(CFGOPT_LOG_LEVEL_STDERR));
# Load module dynamically
require pgBackRest::Protocol::Local::Minion;
pgBackRest::Protocol::Local::Minion->import();
# Create the local object
my $oLocal = new pgBackRest::Protocol::Local::Minion();
# Process local requests
exitSafe($oLocal->process());
}
################################################################################################################################
# Process check command
################################################################################################################################
if (cfgCommandTest(CFGCMD_CHECK))
{
# Load module dynamically
require pgBackRest::Check::Check;
pgBackRest::Check::Check->import();
exitSafe(new pgBackRest::Check::Check()->process());
}
################################################################################################################################
# Process start/stop commands
################################################################################################################################
if (cfgCommandTest(CFGCMD_START))
{
lockStart();
exitSafe(0);
}
elsif (cfgCommandTest(CFGCMD_STOP))
{
lockStop();
exitSafe(0);
}
# Check that the repo path exists
require pgBackRest::Protocol::Storage::Helper;
pgBackRest::Protocol::Storage::Helper->import();
if (isRepoLocal() && !cfgOptionTest(CFGOPT_REPO_TYPE, CFGOPTVAL_REPO_TYPE_S3) && !storageRepo()->pathExists(''))
{
confess &log(ERROR, 'repo-path \'' . cfgOption(CFGOPT_REPO_PATH) . '\' does not exist', ERROR_PATH_MISSING);
}
################################################################################################################################
# Process info command
################################################################################################################################
if (cfgCommandTest(CFGCMD_INFO))
{
# Load module dynamically
require pgBackRest::Info;
pgBackRest::Info->import();
exitSafe(new pgBackRest::Info()->process());
}
################################################################################################################################
# Acquire the command lock
################################################################################################################################
lockAcquire(cfgCommandName(cfgCommandGet()));
################################################################################################################################
# Open the log file
################################################################################################################################
require pgBackRest::Storage::Helper;
pgBackRest::Storage::Helper->import();
logFileSet(
storageLocal(), cfgOption(CFGOPT_LOG_PATH) . '/' . cfgOption(CFGOPT_STANZA) . '-' . lc(cfgCommandName(cfgCommandGet())));
################################################################################################################################
# Process stanza-create command
################################################################################################################################
if (cfgCommandTest(CFGCMD_STANZA_CREATE) || cfgCommandTest(CFGCMD_STANZA_UPGRADE))
{
if (!isRepoLocal())
{
confess &log(ERROR, cfgCommandName(cfgCommandGet()) . ' command must be run on the backup host', ERROR_HOST_INVALID);
}
# Load module dynamically
require pgBackRest::Stanza;
pgBackRest::Stanza->import();
exitSafe(new pgBackRest::Stanza()->process());
}
################################################################################################################################
# RESTORE
################################################################################################################################
if (cfgCommandTest(CFGCMD_RESTORE))
{
if (!isDbLocal())
{
confess &log(ERROR, 'restore command must be run on the db host', ERROR_HOST_INVALID);
}
# Load module dynamically
require pgBackRest::Restore;
pgBackRest::Restore->import();
# Do the restore
new pgBackRest::Restore()->process();
exitSafe(0);
}
else
{
############################################################################################################################
# Make sure backup and expire commands happen on the backup side
############################################################################################################################
if (!isRepoLocal())
{
confess &log(ERROR, 'backup and expire commands must be run on the backup host', ERROR_HOST_INVALID);
}
############################################################################################################################
# BACKUP
############################################################################################################################
if (cfgCommandTest(CFGCMD_BACKUP))
{
# Load module dynamically
require pgBackRest::Backup::Backup;
pgBackRest::Backup::Backup->import();
new pgBackRest::Backup::Backup()->process();
cfgCommandSet(CFGCMD_EXPIRE);
}
############################################################################################################################
# EXPIRE
############################################################################################################################
if (cfgCommandTest(CFGCMD_EXPIRE))
{
# Load module dynamically
require pgBackRest::Expire;
pgBackRest::Expire->import();
new pgBackRest::Expire()->process();
}
}
lockRelease();
exitSafe(0);
# uncoverable statement - exit should happen above
&log(ASSERT, 'execution reached invalid location in ' . __FILE__ . ', line ' . __LINE__);
exit ERROR_ASSERT; # uncoverable statement
}
####################################################################################################################################
# Check for errors
####################################################################################################################################
or do
{
# Perl 5.10 seems to have a problem propogating errors up through a large call stack, so in the case that the error arrives
# blank just use the last logged error instead. Don't do this in all cases because newer Perls seem to work fine and there are
# other errors that could be arriving in $EVAL_ERROR.
exitSafe(undef, defined($EVAL_ERROR) && length($EVAL_ERROR) > 0 ? $EVAL_ERROR : logErrorLast());
};
# uncoverable statement - errors should be handled in the do block above
&log(ASSERT, 'execution reached invalid location in ' . __FILE__ . ', line ' . __LINE__);
exit ERROR_ASSERT; # uncoverable statement

View File

@@ -16,6 +16,10 @@
<release-item>
<p>The C library is now required. This eliminates conditional loading and eases development of new library features.</p>
</release-item>
<release-item>
<p>The <file>{[project-exe]}</file> executable is now a C binary instead of Perl. This allows certain time-critical commands (like async <cmd>archive-push</cmd>) to run more quickly.</p>
</release-item>
</release-improvement-list>
<release-development-list>

View File

@@ -234,9 +234,6 @@
<execute user="root" show="n">
<exe-cmd>mkdir /root/pgbackrest-release-{[version]}</exe-cmd>
</execute>
<execute user="root" show="n">
<exe-cmd>cp -r /backrest/bin /root/pgbackrest-release-{[version]}</exe-cmd>
</execute>
<execute user="root" show="n">
<exe-cmd>cp -r /backrest/build /root/pgbackrest-release-{[version]}</exe-cmd>
</execute>
@@ -264,12 +261,6 @@
<execute user="root">
<exe-cmd>find {[perl-lib-path]}/pgBackRest -type d -exec chmod 755 {} +</exe-cmd>
</execute>
<execute user="root">
<exe-cmd>cp /root/pgbackrest-release-{[version]}/bin/{[project-exe]} {[perl-bin-path]}/{[project-exe]}</exe-cmd>
</execute>
<execute user="root">
<exe-cmd>chmod 755 {[perl-bin-path]}/{[project-exe]}</exe-cmd>
</execute>
<execute user="root">
<exe-cmd>mkdir -m 770 /var/log/pgbackrest</exe-cmd>
</execute>
@@ -287,8 +278,8 @@
</execute>
</execute-list>
<!-- LibC installation - disabled for better testing of the C/Perl failback mechanism -->
<p><backrest/> includes an optional companion C library that enhances performance and enables the `checksum-page` option and encryption. Pre-built packages are generally a better option than building the C library manually but the steps required are given below for completeness. Depending on the distribution a number of packages may be required which will not be enumerated here.</p>
<!-- LibC installation -->
<p><backrest/> includes a companion C library that enhances performance and enables the `checksum-page` option and encryption. Pre-built packages are generally a better option than building the C library manually but the steps required are given below for completeness. Depending on the distribution a number of packages may be required which will not be enumerated here.</p>
<execute-list host="{[br-install-host]}">
<title>Build and Install C Library</title>
@@ -304,6 +295,20 @@
<exe-cmd>make -C /root/pgbackrest-release-{[version]}/libc install</exe-cmd>
</execute>
</execute-list>
<!-- Bin installation -->
<p>Although most of <backrest/> is written in Perl, the main executable is written in C. This allows certain time-critical commands (like async <cmd>archive-push</cmd>) to run more quickly.</p>
<execute-list host="{[br-install-host]}">
<title>Build and Install Binary</title>
<execute user="root" err-suppress="y">
<exe-cmd>make -C /root/pgbackrest-release-{[version]}/src</exe-cmd>
</execute>
<execute user="root">
<exe-cmd>make -C /root/pgbackrest-release-{[version]}/src install</exe-cmd>
</execute>
</execute-list>
</block-define>
<block-define id="br-install-repo">

View File

@@ -43,7 +43,7 @@ sub new
logDebugParam
(
__PACKAGE__ . '->new', \@_,
{name => 'strBackRestBin', default => BACKREST_BIN, trace => true},
{name => 'strBackRestBin', default => backrestBin(), trace => true},
);
# Return from function and log return values if any

View File

@@ -56,7 +56,7 @@ sub new
__PACKAGE__ . '->new', \@_,
{name => 'strWalPath'},
{name => 'strSpoolPath'},
{name => 'strBackRestBin', default => BACKREST_BIN},
{name => 'strBackRestBin', default => backrestBin()},
);
# Return from function and log return values if any

View File

@@ -187,7 +187,7 @@ sub configLoad
# calculated correctly in the C Library -- perhaps in the future this value will be passed in or set some other way
if (cfgOptionValid(CFGOPT_BACKUP_CMD) && cfgOptionTest(CFGOPT_BACKUP_HOST) && !cfgOptionTest(CFGOPT_BACKUP_CMD))
{
cfgOptionSet(CFGOPT_BACKUP_CMD, BACKREST_BIN);
cfgOptionSet(CFGOPT_BACKUP_CMD, backrestBin());
$oOption{cfgOptionName(CFGOPT_BACKUP_CMD)}{source} = CFGDEF_SOURCE_DEFAULT;
}
@@ -198,7 +198,7 @@ sub configLoad
if (cfgOptionTest(cfgOptionIdFromIndex(CFGOPT_DB_HOST, $iOptionIdx)) &&
!cfgOptionTest(cfgOptionIdFromIndex(CFGOPT_DB_CMD, $iOptionIdx)))
{
cfgOptionSet(cfgOptionIdFromIndex(CFGOPT_DB_CMD, $iOptionIdx), BACKREST_BIN);
cfgOptionSet(cfgOptionIdFromIndex(CFGOPT_DB_CMD, $iOptionIdx), backrestBin());
$oOption{cfgOptionIdFromIndex(CFGOPT_DB_CMD, $iOptionIdx)}{source} = CFGDEF_SOURCE_DEFAULT;
}
}
@@ -1153,7 +1153,7 @@ sub cfgCommandWrite
my $bDisplayOnly = shift;
# Set defaults
$strExeString = defined($strExeString) ? $strExeString : BACKREST_BIN;
$strExeString = defined($strExeString) ? $strExeString : backrestBin();
$bIncludeConfig = defined($bIncludeConfig) ? $bIncludeConfig : false;
$bIncludeCommand = defined($bIncludeCommand) ? $bIncludeCommand : true;

308
lib/pgBackRest/Main.pm Normal file
View File

@@ -0,0 +1,308 @@
####################################################################################################################################
# pgBackRest - Reliable PostgreSQL Backup & Restore
####################################################################################################################################
package pgBackRest::Main;
####################################################################################################################################
# Perl includes
####################################################################################################################################
use strict;
use warnings FATAL => qw(all);
use Carp qw(confess);
use English '-no_match_vars';
# Convert die to confess to capture the stack trace
$SIG{__DIE__} = sub { Carp::confess @_ };
use File::Basename qw(dirname);
use pgBackRest::Common::Exception;
use pgBackRest::Common::Exit;
use pgBackRest::Common::Lock;
use pgBackRest::Common::Log;
use pgBackRest::Config::Config;
use pgBackRest::Protocol::Helper;
use pgBackRest::Version;
####################################################################################################################################
# Main entry point for the library
####################################################################################################################################
sub main
{
my $strBackRestBin = shift;
@ARGV = @_;
################################################################################################################################
# Run in eval block to catch errors
################################################################################################################################
eval
{
############################################################################################################################
# Load command line parameters and config
############################################################################################################################
backrestBinSet($strBackRestBin);
my $bConfigResult = configLoad();
# Display help and version
if (cfgCommandTest(CFGCMD_HELP) || cfgCommandTest(CFGCMD_VERSION))
{
# Load module dynamically
require pgBackRest::Config::ConfigHelp;
pgBackRest::Config::ConfigHelp->import();
# Generate help and exit
configHelp($ARGV[1], $ARGV[2], cfgCommandTest(CFGCMD_VERSION), $bConfigResult);
exitSafe(0);
}
# Set test options
if (cfgOptionTest(CFGOPT_TEST) && cfgOption(CFGOPT_TEST))
{
testSet(cfgOption(CFGOPT_TEST), cfgOption(CFGOPT_TEST_DELAY), cfgOption(CFGOPT_TEST_POINT, false));
}
############################################################################################################################
# Process archive-push command
############################################################################################################################
if (cfgCommandTest(CFGCMD_ARCHIVE_PUSH))
{
# Load module dynamically
require pgBackRest::Archive::Push::Push;
pgBackRest::Archive::Push::Push->import();
exitSafe(new pgBackRest::Archive::Push::Push()->process($ARGV[1]));
}
############################################################################################################################
# Process archive-get command
############################################################################################################################
if (cfgCommandTest(CFGCMD_ARCHIVE_GET))
{
# Load module dynamically
require pgBackRest::Archive::Get::Get;
pgBackRest::Archive::Get::Get->import();
exitSafe(new pgBackRest::Archive::Get::Get()->process());
}
############################################################################################################################
# Process remote commands
############################################################################################################################
if (cfgCommandTest(CFGCMD_REMOTE))
{
# Set log levels
cfgOptionSet(CFGOPT_LOG_LEVEL_STDERR, PROTOCOL, true);
logLevelSet(OFF, OFF, cfgOption(CFGOPT_LOG_LEVEL_STDERR));
if (cfgOptionTest(CFGOPT_TYPE, CFGOPTVAL_REMOTE_TYPE_BACKUP) &&
!cfgOptionTest(CFGOPT_REPO_TYPE, CFGOPTVAL_REPO_TYPE_S3) &&
!-e cfgOption(CFGOPT_REPO_PATH))
{
confess &log(ERROR, 'repo-path \'' . cfgOption(CFGOPT_REPO_PATH) . '\' does not exist', ERROR_PATH_MISSING);
}
# Load module dynamically
require pgBackRest::Protocol::Remote::Minion;
pgBackRest::Protocol::Remote::Minion->import();
# Create the remote object
my $oRemote = new pgBackRest::Protocol::Remote::Minion(
cfgOption(CFGOPT_BUFFER_SIZE), cfgOption(CFGOPT_PROTOCOL_TIMEOUT));
# Acquire a remote lock (except for commands that are read-only or local processes)
my $strLockName;
if (!(cfgOptionTest(CFGOPT_COMMAND, cfgCommandName(CFGCMD_ARCHIVE_GET)) ||
cfgOptionTest(CFGOPT_COMMAND, cfgCommandName(CFGCMD_INFO)) ||
cfgOptionTest(CFGOPT_COMMAND, cfgCommandName(CFGCMD_RESTORE)) ||
cfgOptionTest(CFGOPT_COMMAND, cfgCommandName(CFGCMD_CHECK)) ||
cfgOptionTest(CFGOPT_COMMAND, cfgCommandName(CFGCMD_LOCAL))))
{
$strLockName = cfgOption(CFGOPT_COMMAND);
}
# Process remote requests
exitSafe($oRemote->process($strLockName));
}
############################################################################################################################
# Process local commands
############################################################################################################################
if (cfgCommandTest(CFGCMD_LOCAL))
{
# Set log levels
cfgOptionSet(CFGOPT_LOG_LEVEL_STDERR, PROTOCOL, true);
logLevelSet(OFF, OFF, cfgOption(CFGOPT_LOG_LEVEL_STDERR));
# Load module dynamically
require pgBackRest::Protocol::Local::Minion;
pgBackRest::Protocol::Local::Minion->import();
# Create the local object
my $oLocal = new pgBackRest::Protocol::Local::Minion();
# Process local requests
exitSafe($oLocal->process());
}
############################################################################################################################
# Process check command
############################################################################################################################
if (cfgCommandTest(CFGCMD_CHECK))
{
# Load module dynamically
require pgBackRest::Check::Check;
pgBackRest::Check::Check->import();
exitSafe(new pgBackRest::Check::Check()->process());
}
############################################################################################################################
# Process start/stop commands
############################################################################################################################
if (cfgCommandTest(CFGCMD_START))
{
lockStart();
exitSafe(0);
}
elsif (cfgCommandTest(CFGCMD_STOP))
{
lockStop();
exitSafe(0);
}
# Check that the repo path exists
require pgBackRest::Protocol::Storage::Helper;
pgBackRest::Protocol::Storage::Helper->import();
if (isRepoLocal() && !cfgOptionTest(CFGOPT_REPO_TYPE, CFGOPTVAL_REPO_TYPE_S3) && !storageRepo()->pathExists(''))
{
confess &log(ERROR, 'repo-path \'' . cfgOption(CFGOPT_REPO_PATH) . '\' does not exist', ERROR_PATH_MISSING);
}
############################################################################################################################
# Process info command
############################################################################################################################
if (cfgCommandTest(CFGCMD_INFO))
{
# Load module dynamically
require pgBackRest::Info;
pgBackRest::Info->import();
exitSafe(new pgBackRest::Info()->process());
}
############################################################################################################################
# Acquire the command lock
############################################################################################################################
lockAcquire(cfgCommandName(cfgCommandGet()));
############################################################################################################################
# Open the log file
############################################################################################################################
require pgBackRest::Storage::Helper;
pgBackRest::Storage::Helper->import();
logFileSet(
storageLocal(),
cfgOption(CFGOPT_LOG_PATH) . '/' . cfgOption(CFGOPT_STANZA) . '-' . lc(cfgCommandName(cfgCommandGet())));
############################################################################################################################
# Process stanza-create command
############################################################################################################################
if (cfgCommandTest(CFGCMD_STANZA_CREATE) || cfgCommandTest(CFGCMD_STANZA_UPGRADE))
{
if (!isRepoLocal())
{
confess &log(ERROR,
cfgCommandName(cfgCommandGet()) . ' command must be run on the backup host', ERROR_HOST_INVALID);
}
# Load module dynamically
require pgBackRest::Stanza;
pgBackRest::Stanza->import();
exitSafe(new pgBackRest::Stanza()->process());
}
############################################################################################################################
# RESTORE
############################################################################################################################
if (cfgCommandTest(CFGCMD_RESTORE))
{
if (!isDbLocal())
{
confess &log(ERROR, 'restore command must be run on the db host', ERROR_HOST_INVALID);
}
# Load module dynamically
require pgBackRest::Restore;
pgBackRest::Restore->import();
# Do the restore
new pgBackRest::Restore()->process();
exitSafe(0);
}
else
{
########################################################################################################################
# Make sure backup and expire commands happen on the backup side
########################################################################################################################
if (!isRepoLocal())
{
confess &log(ERROR, 'backup and expire commands must be run on the backup host', ERROR_HOST_INVALID);
}
########################################################################################################################
# BACKUP
########################################################################################################################
if (cfgCommandTest(CFGCMD_BACKUP))
{
# Load module dynamically
require pgBackRest::Backup::Backup;
pgBackRest::Backup::Backup->import();
new pgBackRest::Backup::Backup()->process();
cfgCommandSet(CFGCMD_EXPIRE);
}
########################################################################################################################
# EXPIRE
########################################################################################################################
if (cfgCommandTest(CFGCMD_EXPIRE))
{
# Load module dynamically
require pgBackRest::Expire;
pgBackRest::Expire->import();
new pgBackRest::Expire()->process();
}
}
lockRelease();
exitSafe(0);
# uncoverable statement - exit should happen above
&log(ASSERT, 'execution reached invalid location in ' . __FILE__ . ', line ' . __LINE__);
exit ERROR_ASSERT; # uncoverable statement
}
################################################################################################################################
# Check for errors
################################################################################################################################
or do
{
# Perl 5.10 seems to have a problem propogating errors up through a large call stack, so in the case that the error arrives
# blank just use the last logged error instead. Don't do this in all cases because newer Perls seem to work fine and there
# are other errors that could be arriving in $EVAL_ERROR.
exitSafe(undef, defined($EVAL_ERROR) && length($EVAL_ERROR) > 0 ? $EVAL_ERROR : logErrorLast());
};
# uncoverable statement - errors should be handled in the do block above
&log(ASSERT, 'execution reached invalid location in ' . __FILE__ . ', line ' . __LINE__);
exit ERROR_ASSERT; # uncoverable statement
}
1;

View File

@@ -41,7 +41,7 @@ sub new
__PACKAGE__ . '->new', \@_,
{name => 'strHostType'},
{name => 'iSelectTimeout', default => int(cfgOption(CFGOPT_PROTOCOL_TIMEOUT) / 2)},
{name => 'strBackRestBin', default => BACKREST_BIN},
{name => 'strBackRestBin', default => backrestBin()},
{name => 'bConfessError', default => true},
);

View File

@@ -27,8 +27,12 @@ use constant BACKREST_CONF => BACKREST_
#
# Stores the exe location.
#-----------------------------------------------------------------------------------------------------------------------------------
use constant BACKREST_BIN => abs_path($0);
push @EXPORT, qw(BACKREST_BIN);
my $strBackRestBin;
sub backrestBin {return $strBackRestBin};
sub backrestBinSet {$strBackRestBin = shift}
push @EXPORT, qw(backrestBin backrestBinSet);
# BackRest Version Number
#

14
src/Makefile Normal file
View File

@@ -0,0 +1,14 @@
CC=gcc
CFLAGS=-I. -Wfatal-errors -Wall -Wextra -Wwrite-strings -Wno-clobbered -std=c99 -funroll-loops -ftree-vectorize
DESTDIR=
pgbackrest: main.o \
common/error.o common/errorType.o common/memContext.o common/type/list.o common/type/string.o common/type/stringList.o \
perl/exec.o
$(CC) $(CFLAGS) -o pgbackrest main.o \
common/error.o common/errorType.o common/memContext.o common/type/list.o common/type/string.o common/type/stringList.o \
perl/exec.o
install: pgbackrest
sudo install -d $(DESTDIR)/usr/bin
sudo install -m 755 pgbackrest $(DESTDIR)/usr/bin

10
src/main.c Normal file
View File

@@ -0,0 +1,10 @@
/***********************************************************************************************************************************
Main
***********************************************************************************************************************************/
#include "perl/exec.h"
int main(int argListSize, char *argList[])
{
// Execute Perl since nothing is implemented in C (yet)
perlExec(perlCommand(argListSize, argList));
}

104
src/perl/exec.c Normal file
View File

@@ -0,0 +1,104 @@
/***********************************************************************************************************************************
Execute Perl for Legacy Functionality
***********************************************************************************************************************************/
#include <errno.h>
#include <getopt.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "version.h"
#include "common/error.h"
#include "common/memContext.h"
#include "common/type.h"
/***********************************************************************************************************************************
Constants used to build perl options
***********************************************************************************************************************************/
#define PERL_EXE "perl"
#define ENV_EXE "/usr/bin/env"
#define PARAM_PERL_OPTION "perl-option"
#define PARAM_PERL_OPTION_ID 1000
#define PGBACKREST_MODULE PGBACKREST_NAME "::Main"
#define PGBACKREST_MAIN PGBACKREST_MODULE "::main"
/***********************************************************************************************************************************
Build list of perl options to use for exec
***********************************************************************************************************************************/
StringList *perlCommand(int argListSize, char *argList[])
{
// Setup arg list for perl exec
StringList *perlArgList = strLstNew();
strLstAdd(perlArgList, strNew(ENV_EXE));
strLstAdd(perlArgList, strNew(PERL_EXE));
// Setup Perl main call
String *mainParamBuffer = strNew("");
// Setup pgbackrest bin
String *binParamBuffer = strNew("");
// Reset optind to 1 in case getopt_long has been called before
optind = 1;
// Struct with all valid options
static struct option optionList[] =
{
{PARAM_PERL_OPTION, required_argument, NULL, PARAM_PERL_OPTION_ID},
{0, 0, NULL, 0},
};
// Parse options
int option;
int optionIdx;
opterr = false;
while ((option = getopt_long(argListSize, argList, "-", optionList, &optionIdx)) != -1)
{
switch (option)
{
case 1:
case '?':
strCat(mainParamBuffer, ", ");
strCatFmt(mainParamBuffer, "'%s'", argList[optind - 1]);
break;
case PARAM_PERL_OPTION_ID:
strLstAdd(perlArgList, strNew(optarg));
strCatFmt(binParamBuffer, " --" PARAM_PERL_OPTION "=\"%s\"", optarg);
break;
}
}
// Finish Perl main call
String *mainBuffer = strNewFmt(
PGBACKREST_MAIN "('%s%s'%s)", argList[0], strPtr(binParamBuffer), strPtr(mainParamBuffer));
strFree(binParamBuffer);
strFree(mainParamBuffer);
// Finish arg list for perl exec
strLstAdd(perlArgList, strNew("-M" PGBACKREST_MODULE));
strLstAdd(perlArgList, strNew("-e"));
strLstAdd(perlArgList, mainBuffer);
strLstAdd(perlArgList, NULL);
return perlArgList;
}
/***********************************************************************************************************************************
Exec supplied Perl options
***********************************************************************************************************************************/
void perlExec(StringList *perlArgList)
{
// Exec perl with supplied arguments
execvp(strPtr(strLstGet(perlArgList, 0)), (char **)strLstPtr(perlArgList));
// The previous command only returns on error so throw it
int errNo = errno;
THROW(AssertError, "unable to exec %s: %s", strPtr(strLstGet(perlArgList, 0)), strerror(errNo));
} // {uncoverable - perlExec() does not return}

15
src/perl/exec.h Normal file
View File

@@ -0,0 +1,15 @@
/***********************************************************************************************************************************
Execute Perl for Legacy Functionality
***********************************************************************************************************************************/
#ifndef PERL_EXEC_H
#define PERL_EXEC_H
#include "common/type.h"
/***********************************************************************************************************************************
Functions
***********************************************************************************************************************************/
StringList *perlCommand(int argListSize, char *argList[]);
void perlExec(StringList *perlArgList);
#endif

12
src/version.h Normal file
View File

@@ -0,0 +1,12 @@
/***********************************************************************************************************************************
Version Numbers and Names
***********************************************************************************************************************************/
#ifndef VERSION_H
#define VERSION_H
/***********************************************************************************************************************************
Official name of the software, also used for Perl package name
***********************************************************************************************************************************/
#define PGBACKREST_NAME "pgBackRest"
#endif

View File

@@ -265,6 +265,25 @@ my $oTestDef =
},
]
},
# Perl tests
{
&TESTDEF_NAME => 'perl',
&TESTDEF_CONTAINER => true,
&TESTDEF_TEST =>
[
{
&TESTDEF_NAME => 'exec',
&TESTDEF_TOTAL => 2,
&TESTDEF_C => true,
&TESTDEF_COVERAGE =>
{
'perl/exec' => TESTDEF_COVERAGE_FULL,
},
},
]
},
# Help tests
{
&TESTDEF_NAME => 'help',

View File

@@ -22,6 +22,7 @@ use pgBackRest::DbVersion;
use pgBackRest::Common::Exception;
use pgBackRest::Common::Log;
use pgBackRest::Common::String;
use pgBackRest::Version;
use pgBackRestTest::Common::ContainerTest;
use pgBackRestTest::Common::DefineTest;
@@ -164,21 +165,11 @@ sub run
"-test",
{bSuppressStdErr => true});
# Install Perl C Library
my $oVm = vmGet();
my $strOS = $self->{oTest}->{&TEST_VM};
my $strBuildPath = $self->{strBackRestBase} . "/test/.vagrant/libc/$strOS/libc/";
my $strPerlAutoPath = $$oVm{$strOS}{&VMDEF_PERL_ARCH_PATH} . '/auto/pgBackRest/LibC';
my $strPerlModulePath = $$oVm{$strOS}{&VMDEF_PERL_ARCH_PATH} . '/pgBackRest';
executeTest(
"docker exec -i -u root ${strImage} bash -c '" .
"mkdir -p -m 755 ${strPerlAutoPath} && " .
"cp ${strBuildPath}/blib/arch/auto/pgBackRest/LibC/LibC.so ${strPerlAutoPath} && " .
"cp ${strBuildPath}/blib/lib/auto/pgBackRest/LibC/autosplit.ix ${strPerlAutoPath} && " .
"mkdir -p -m 755 ${strPerlModulePath} && " .
"cp ${strBuildPath}/blib/lib/pgBackRest/LibC.pm ${strPerlModulePath} && " .
"cp ${strBuildPath}/blib/lib/pgBackRest/LibCAuto.pm ${strPerlModulePath}'");
# Install bin and Perl C Library
if (!$self->{oTest}->{&TEST_C})
{
jobInstallC($self->{strBackRestBase}, $self->{oTest}->{&TEST_VM}, $strImage);
}
}
}
@@ -201,10 +192,10 @@ sub run
{
$strCommand =
($self->{oTest}->{&TEST_CONTAINER} ? 'docker exec -i -u ' . TEST_USER . " ${strImage} " : '') .
(vmCoverage($self->{oTest}->{&TEST_VM}) ? testRunExe(
abs_path($0), dirname($self->{strCoveragePath}), $self->{strBackRestBase}, $self->{oTest}->{&TEST_MODULE},
$self->{oTest}->{&TEST_NAME}, defined($self->{oTest}->{&TEST_RUN}) ? $self->{oTest}->{&TEST_RUN} : 'all') :
abs_path($0)) .
testRunExe(
vmCoverage($self->{oTest}->{&TEST_VM}), undef, abs_path($0),
dirname($self->{strCoveragePath}), $self->{strBackRestBase}, $self->{oTest}->{&TEST_MODULE},
$self->{oTest}->{&TEST_NAME}, defined($self->{oTest}->{&TEST_RUN}) ? $self->{oTest}->{&TEST_RUN} : 'all') .
" --test-path=${strVmTestPath}" .
" --vm=$self->{oTest}->{&TEST_VM}" .
" --vm-id=$self->{iVmIdx}" .
@@ -224,13 +215,7 @@ sub run
if (!$self->{bDryRun} || $self->{bVmOut})
{
# Set permissions on the Docker test directory. This can be removed once users/groups are sync'd between
# Docker and the host VM.
if ($self->{oTest}->{&TEST_CONTAINER})
{
executeTest("docker exec ${strImage} chown " . TEST_USER . ':' . TEST_GROUP . " -R ${strVmTestPath}");
}
# If testing C code
if ($self->{oTest}->{&TEST_C})
{
my $strCSrcPath = "$self->{strBackRestBase}/src";
@@ -244,6 +229,9 @@ sub run
# Skip all files except .c files (including .auto.c)
next if $strFile !~ /(?<!\.auto)\.c$/;
# !!! Skip main for now until it can be rewritten
next if $strFile =~ /main\.c$/;
if (!defined($hTestCoverage->{substr($strFile, 0, length($strFile) - 2)}))
{
push(@stryCFile, "${strCSrcPath}/${strFile}");
@@ -293,8 +281,6 @@ sub run
{
my $bSelected = false;
# use Data::Dumper; confess "RUN: " . Dumper($self->{oTest}->{&TEST_RUN});
if (!defined($self->{oTest}->{&TEST_RUN}) || @{$self->{oTest}->{&TEST_RUN}} == 0 ||
grep(/^$iTestIdx$/, @{$self->{oTest}->{&TEST_RUN}}))
{
@@ -336,9 +322,7 @@ sub run
{
exec => $oExec,
test => $strTest,
# idx => $self->{iTestIdx},
# container => $self->{oTest}->{&TEST_CONTAINER},
start_time => $fTestStartTime
start_time => $fTestStartTime,
};
$bRun = true;
@@ -501,4 +485,37 @@ sub end
);
}
####################################################################################################################################
# Install C binary and library
####################################################################################################################################
sub jobInstallC
{
my $strBasePath = shift;
my $strVm = shift;
my $strImage = shift;
# Install Perl C Library
my $oVm = vmGet();
my $strBuildPath = "${strBasePath}/test/.vagrant";
my $strBuildLibCPath = "$strBuildPath/libc/${strVm}/libc";
my $strBuildBinPath = "$strBuildPath/bin/${strVm}/src";
my $strPerlAutoPath = $oVm->{$strVm}{&VMDEF_PERL_ARCH_PATH} . '/auto/pgBackRest/LibC';
my $strPerlModulePath = $oVm->{$strVm}{&VMDEF_PERL_ARCH_PATH} . '/pgBackRest';
executeTest(
"docker exec -i -u root ${strImage} bash -c '" .
"mkdir -p -m 755 ${strPerlAutoPath} && " .
# "cp ${strBuildLibCPath}/blib/arch/auto/pgBackRest/LibC/LibC.bs ${strPerlAutoPath} && " .
"cp ${strBuildLibCPath}/blib/arch/auto/pgBackRest/LibC/LibC.so ${strPerlAutoPath} && " .
"cp ${strBuildLibCPath}/blib/lib/auto/pgBackRest/LibC/autosplit.ix ${strPerlAutoPath} && " .
"mkdir -p -m 755 ${strPerlModulePath} && " .
"cp ${strBuildLibCPath}/blib/lib/pgBackRest/LibC.pm ${strPerlModulePath} && " .
"cp ${strBuildLibCPath}/blib/lib/pgBackRest/LibCAuto.pm ${strPerlModulePath} && " .
"cp ${strBuildBinPath}/" . BACKREST_EXE . ' /usr/bin/' . BACKREST_EXE . ' && ' .
'chmod 755 /usr/bin/' . BACKREST_EXE . ' && ' .
"ln -s ${strBasePath}/lib/pgBackRest /usr/share/perl5/pgBackRest'");
}
push(@EXPORT, qw(jobInstallC));
1;

View File

@@ -21,6 +21,7 @@ use pgBackRest::Common::String;
use pgBackRest::Common::Wait;
use pgBackRest::Storage::Posix::Driver;
use pgBackRest::Storage::Local;
use pgBackRest::Version;
use pgBackRestTest::Common::DefineTest;
use pgBackRestTest::Common::ExecuteTest;
@@ -112,7 +113,8 @@ sub process
$self->{iVmId},
$self->{strBasePath},
$self->{strTestPath},
$self->{strBackRestExeOriginal},
$self->{strBackRestExeC},
$self->{strBackRestExeHelper},
$self->{strPgBinPath},
$self->{strPgVersion},
$self->{strModule},
@@ -133,7 +135,8 @@ sub process
{name => 'iVmId'},
{name => 'strBasePath'},
{name => 'strTestPath'},
{name => 'strBackRestExeOriginal'},
{name => 'strBackRestExeC'},
{name => 'strBackRestExeHelper'},
{name => 'strPgBinPath', required => false},
{name => 'strPgVersion', required => false},
{name => 'strModule'},
@@ -233,27 +236,23 @@ sub begin
return false;
}
my $strExe = $self->backrestExeOriginal();
# Generate backrest exe
$self->{strBackRestExe} = testRunExe(
$self->coverage(), $self->{strBackRestExeC}, $self->{strBackRestExeHelper}, dirname($self->testPath()), $self->basePath(),
$self->module(), $self->moduleTest(), $self->runCurrent(), true);
# If coverage is requested then prepend the coverage code
if ($self->coverage())
{
$strExe = testRunExe(
$strExe, dirname($self->testPath()), $self->basePath(), $self->module(), $self->moduleTest(), $self->runCurrent(),
true);
}
backrestBinSet($self->{strBackRestExe});
# Create an ExpectTest object
if ($self->doExpect())
{
$self->{oExpect} = new pgBackRestTest::Common::LogTest(
$self->module(), $self->moduleTest(), $self->runCurrent(), $self->doLogForce(), $strDescription, $strExe,
$self->pgBinPath(), $self->testPath());
$self->module(), $self->moduleTest(), $self->runCurrent(), $self->doLogForce(), $strDescription,
$self->{strBackRestExe}, $self->pgBinPath(), $self->testPath());
&log(INFO, ' expect log: ' . $self->{oExpect}->{strFileName});
}
$self->{strBackRestExe} = $strExe;
if (!$self->{bFirstTest})
{
@@ -537,7 +536,9 @@ push @EXPORT, qw(testRunGet);
####################################################################################################################################
sub testRunExe
{
my $strExe = shift;
my $bCoverage = shift;
my $strExeC = shift;
my $strExeHelper = shift;
my $strCoveragePath = shift;
my $strBackRestBasePath = shift;
my $strModule = shift;
@@ -545,31 +546,43 @@ sub testRunExe
my $iRun = shift;
my $bLog = shift;
# Limit Perl modules tested to what is defined in the test coverage (if it exists)
my $hTestCoverage = (testDefModuleTest($strModule, $strTest))->{&TESTDEF_COVERAGE};
my $strExe = defined($strExeC) ? $strExeC : undef;
my $strPerlModule;
my $strPerlModuleLog;
if (defined($hTestCoverage))
if ($bCoverage)
{
foreach my $strCoverageModule (sort(keys(%{$hTestCoverage})))
# Limit Perl modules tested to what is defined in the test coverage (if it exists)
my $hTestCoverage = (testDefModuleTest($strModule, $strTest))->{&TESTDEF_COVERAGE};
my $strPerlModuleLog;
if (defined($hTestCoverage))
{
$strPerlModule .= ',.*/' . $strCoverageModule . '\.p.$';
$strPerlModuleLog .= (defined($strPerlModuleLog) ? ', ' : '') . $strCoverageModule;
foreach my $strCoverageModule (sort(keys(%{$hTestCoverage})))
{
$strPerlModule .= ',.*/' . $strCoverageModule . '\.p.$';
$strPerlModuleLog .= (defined($strPerlModuleLog) ? ', ' : '') . $strCoverageModule;
}
}
# Build the exe
if (defined($strPerlModule))
{
$strExe .=
(defined($strExeC) ? ' --perl-option=' : 'perl ') .
"-MDevel::Cover=-silent,1,-dir,${strCoveragePath},-select${strPerlModule},+inc" .
",${strBackRestBasePath},-coverage,statement,branch,condition,path,subroutine" .
(defined($strExeC) ? '' : " ${strExeHelper}");
if (defined($bLog) && $bLog)
{
&log(INFO, " coverage: ${strPerlModuleLog}");
}
}
}
# Build the exe
if (defined($strPerlModule))
if (!defined($strExeC) && !defined($strPerlModule))
{
$strExe =
"perl -MDevel::Cover=-silent,1,-dir,${strCoveragePath},-select${strPerlModule},+inc,${strBackRestBasePath}" .
",-coverage,statement,branch,condition,path,subroutine ${strExe}";
if (defined($bLog) && $bLog)
{
&log(INFO, " coverage: ${strPerlModuleLog}");
}
$strExe = $strExeHelper;
}
return $strExe;
@@ -592,7 +605,6 @@ push(@EXPORT, qw(storageTest));
####################################################################################################################################
sub archBits {return vmArchBits(shift->{strVm})}
sub backrestExe {return shift->{strBackRestExe}}
sub backrestExeOriginal {return shift->{strBackRestExeOriginal}}
sub backrestUser {return shift->{strBackRestUser}}
sub basePath {return shift->{strBasePath}}
sub coverage {vmCoverage(shift->{strVm})}

View File

@@ -22,6 +22,7 @@ use pgBackRest::Version;
use pgBackRestTest::Common::ContainerTest;
use pgBackRestTest::Common::ExecuteTest;
use pgBackRestTest::Common::JobTest;
use pgBackRestTest::Common::RunTest;
use pgBackRestTest::Common::VmTest;
@@ -79,21 +80,8 @@ sub new
# Set permissions on the test path
$self->executeSimple('chown -R ' . $self->userGet() . ':'. TEST_GROUP . ' ' . $self->testPath(), undef, 'root');
# Install Perl C Library
my $oVm = vmGet();
my $strBuildPath = testRunGet()->basePath() . "/test/.vagrant/libc/$self->{strOS}/libc";
my $strPerlAutoPath = $$oVm{$self->{strOS}}{&VMDEF_PERL_ARCH_PATH} . '/auto/pgBackRest/LibC';
my $strPerlModulePath = $$oVm{$self->{strOS}}{&VMDEF_PERL_ARCH_PATH} . '/pgBackRest';
$self->executeSimple(
"mkdir -p -m 755 ${strPerlAutoPath} && " .
# "cp ${strBuildPath}/blib/arch/auto/pgBackRest/LibC/LibC.bs ${strPerlAutoPath} && " .
"cp ${strBuildPath}/blib/arch/auto/pgBackRest/LibC/LibC.so ${strPerlAutoPath} && " .
"cp ${strBuildPath}/blib/lib/auto/pgBackRest/LibC/autosplit.ix ${strPerlAutoPath} && " .
"mkdir -p -m 755 ${strPerlModulePath} && " .
"cp ${strBuildPath}/blib/lib/pgBackRest/LibC.pm ${strPerlModulePath} && " .
"cp ${strBuildPath}/blib/lib/pgBackRest/LibCAuto.pm ${strPerlModulePath}",
undef, 'root');
# Install bin and Perl C Library
jobInstallC(testRunGet()->basePath(), $self->{strOS}, $strContainer);
# Return from function and log return values if any
return logDebugReturn

View File

@@ -77,7 +77,6 @@ sub initTest
sub run
{
my $self = shift;
my $oArchiveBase = new pgBackRest::Archive::Base();
# Define test file
my $strFileContent = 'TESTDATA';
@@ -92,6 +91,8 @@ sub run
################################################################################################################################
if ($self->begin("Archive::Base::getCheck()"))
{
my $oArchiveBase = new pgBackRest::Archive::Base();
# Create and save archive.info file
my $oArchiveInfo = new pgBackRest::Archive::Info(storageRepo()->pathGet(STORAGE_REPO_ARCHIVE), false,
{bLoad => false, bIgnoreMissing => true});

View File

@@ -17,6 +17,7 @@ use Cwd qw(abs_path);
use pgBackRest::Common::Exception;
use pgBackRest::Common::Log;
use pgBackRest::Config::Config;
use pgBackRest::Version;
use pgBackRestTest::Common::RunTest;
@@ -356,8 +357,9 @@ sub run
$self->configTestLoadExpect(cfgCommandName(CFGCMD_RESTORE));
my $strCommand = cfgCommandWrite(CFGCMD_ARCHIVE_GET);
my $strExpectedCommand = abs_path($0) . " --backup-host=db.mydomain.com \"--db1-path=/db path/main\"" .
" --repo-path=/repo --stanza=app " . cfgCommandName(CFGCMD_ARCHIVE_GET);
my $strExpectedCommand =
backrestBin() . " --backup-host=db.mydomain.com \"--db1-path=/db path/main\" --repo-path=/repo --stanza=app " .
cfgCommandName(CFGCMD_ARCHIVE_GET);
if ($strCommand ne $strExpectedCommand)
{
@@ -372,7 +374,7 @@ sub run
$self->optionTestSet(CFGOPT_DB_PATH, '/db');
$self->configTestLoadExpect(cfgCommandName(CFGCMD_BACKUP));
$self->optionTestExpect(CFGOPT_DB_CMD, abs_path($0));
$self->optionTestExpect(CFGOPT_DB_CMD, backrestBin());
}
if ($self->begin(cfgCommandName(CFGCMD_BACKUP) . ' missing option ' . cfgOptionName(CFGOPT_DB_PATH)))

View File

@@ -0,0 +1,22 @@
--- pgbackrest.install
+++ pgbackrest.install
@@ -1,3 +1,2 @@
-bin/pgbackrest usr/bin/
debian/pgbackrest.conf etc/
lib/pgBackRest usr/share/perl5/
--- rules
+++ rules
@@ -22,6 +22,7 @@
${MANTEMPLATE} > ${CURDIR}/doc/output/man/pgbackrest.1
cd $(CURDIR)/libc; perl Makefile.PL PREFIX=/usr NO_PACKLIST=1
make -C $(CURDIR)/libc/
+ make -C $(CURDIR)/src
dh_auto_build
override_dh_auto_test:
@@ -36,4 +37,5 @@
override_dh_auto_install:
make -C libc install DESTDIR=$(CURDIR)/debian/pgbackrest
+ make -C src install DESTDIR=$(CURDIR)/debian/pgbackrest
dh_auto_install

View File

@@ -0,0 +1,70 @@
/***********************************************************************************************************************************
Test Perl Exec
***********************************************************************************************************************************/
#define TEST_ENV_EXE "/usr/bin/env"
#define TEST_PERL_EXE "perl"
#define TEST_BACKREST_EXE "/path/to/pgbackrest"
#define TEST_PERL_MAIN \
"-MpgBackRest::Main|-e|pgBackRest::Main::main('" TEST_BACKREST_EXE ""
/***********************************************************************************************************************************
Test Run
***********************************************************************************************************************************/
void testRun()
{
// -----------------------------------------------------------------------------------------------------------------------------
if (testBegin("perlCommand()"))
{
// -------------------------------------------------------------------------------------------------------------------------
char *cmdLineParam[128];
int cmdLineParamSize = 0;
cmdLineParam[cmdLineParamSize++] = (char *)TEST_BACKREST_EXE;
TEST_RESULT_STR(
strPtr(strLstCat(perlCommand(cmdLineParamSize, cmdLineParam), "|")),
TEST_ENV_EXE "|" TEST_PERL_EXE "|" TEST_PERL_MAIN "')|[NULL]", "simple command");
// -------------------------------------------------------------------------------------------------------------------------
cmdLineParam[cmdLineParamSize++] = (char *)"--option";
TEST_RESULT_STR(
strPtr(strLstCat(perlCommand(cmdLineParamSize, cmdLineParam), "|")),
TEST_ENV_EXE "|" TEST_PERL_EXE "|" TEST_PERL_MAIN "', '--option')|[NULL]", "simple option");
// -------------------------------------------------------------------------------------------------------------------------
cmdLineParam[cmdLineParamSize++] = (char *)"--option2=value";
TEST_RESULT_STR(
strPtr(strLstCat(perlCommand(cmdLineParamSize, cmdLineParam), "|")),
TEST_ENV_EXE "|" TEST_PERL_EXE "|" TEST_PERL_MAIN "', '--option', '--option2=value')|[NULL]",
"option with = before value");
// -------------------------------------------------------------------------------------------------------------------------
cmdLineParam[cmdLineParamSize++] = (char *)"--option3";
cmdLineParam[cmdLineParamSize++] = (char *)"value";
TEST_RESULT_STR(
strPtr(strLstCat(perlCommand(cmdLineParamSize, cmdLineParam), "|")),
TEST_ENV_EXE "|" TEST_PERL_EXE "|" TEST_PERL_MAIN "', '--option', '--option2=value', '--option3', 'value')|[NULL]",
"option with space before value");
// -------------------------------------------------------------------------------------------------------------------------
cmdLineParam[cmdLineParamSize++] = (char *)"--perl-option=-I.";
TEST_RESULT_STR(
strPtr(strLstCat(perlCommand(cmdLineParamSize, cmdLineParam), "|")),
TEST_ENV_EXE "|" TEST_PERL_EXE "|-I.|" TEST_PERL_MAIN " --perl-option=\"-I.\"', '--option', '--option2=value', "
"'--option3', 'value')|[NULL]",
"perl option");
}
// -----------------------------------------------------------------------------------------------------------------------------
if (testBegin("perlExec()"))
{
StringList *param = strLstAdd(strLstAdd(strLstNew(), strNew(BOGUS_STR)), NULL);
TEST_ERROR(perlExec(param), AssertError, "unable to exec BOGUS: No such file or directory");
}
}

View File

@@ -386,23 +386,96 @@ eval
$oStorageTest->pathCreate($strCoveragePath, {strMode => '0770', bIgnoreMissing => true, bCreateParent => true});
}
# Build the C Library and Packages
# Build the binary, library and packages
#---------------------------------------------------------------------------------------------------------------------------
if (!$bDryRun)
{
my $oVm = vmGet();
my $strVagrantPath = "${strBackRestBase}/test/.vagrant";
# Build the binary
#---------------------------------------------------------------------------------------------------------------------------
my $strBinPath = "${strVagrantPath}/bin";
my $strBinSmart = "${strBinPath}/build.timestamp";
my @stryBinSrcPath = ('src');
# Find the lastest modified time for dirs that affect the bin build
my $lTimestampLast = $oStorageBackRest->exists($strBinSmart) ? $oStorageBackRest->info($strBinSmart)->mtime : 0;
foreach my $strBinSrcPath (@stryBinSrcPath)
{
my $hManifest = $oStorageBackRest->manifest($strBinSrcPath);
foreach my $strFile (sort(keys(%{$hManifest})))
{
if ($hManifest->{$strFile}{type} eq 'f' && $hManifest->{$strFile}{modification_time} > $lTimestampLast)
{
$lTimestampLast = $hManifest->{$strFile}{modification_time};
}
}
}
# Rebuild if the modification time of the smart file does equal the last changes in source paths
if (!$bSmart || !$oStorageBackRest->exists($strBinSmart) ||
$oStorageBackRest->info($strBinSmart)->mtime < $lTimestampLast)
{
if ($bSmart)
{
&log(INFO, 'bin dependencies have changed, rebuilding...');
}
executeTest("sudo rm -rf ${strBinPath}");
executeTest('sudo rm -rf /usr/bin/' . BACKREST_EXE);
}
# Loop through VMs to do the C Library builds
my $bLogDetail = $strLogLevel eq 'detail';
my @stryBuildVm = $strVm eq VM_ALL ? VM_LIST : ($strVm);
foreach my $strBuildVM (sort(@stryBuildVm))
{
my $strBuildPath = "${strBinPath}/${strBuildVM}/src";
if (!$oStorageBackRest->pathExists($strBuildPath))
{
&log(INFO, "build bin for ${strBuildVM} (${strBuildPath})");
executeTest(
"docker run -itd -h test-build --name=test-build" .
" -v ${strBackRestBase}:${strBackRestBase} " . containerRepo() . ":${strBuildVM}-build",
{bSuppressStdErr => true});
foreach my $strBinSrcPath (@stryBinSrcPath)
{
$oStorageBackRest->pathCreate(
"${strBinPath}/${strBuildVM}/${strBinSrcPath}", {bIgnoreExists => true, bCreateParent => true});
executeTest("cp -r ${strBackRestBase}/${strBinSrcPath}/* ${strBinPath}/${strBuildVM}/${strBinSrcPath}");
}
executeTest(
"docker exec -i test-build make --silent --directory ${strBuildPath}",
{bShowOutputAsync => $bLogDetail});
executeTest(
"docker exec -i test-build make --silent --directory ${strBuildPath} install",
{bShowOutputAsync => $bLogDetail});
executeTest("docker rm -f test-build");
}
}
# Write files to indicate the last time a build was successful
$oStorageBackRest->put($strBinSmart);
utime($lTimestampLast, $lTimestampLast, $strBinSmart) or
confess "unable to set time for ${strBinSmart}" . (defined($!) ? ":$!" : '');
# Build the C Library
#---------------------------------------------------------------------------------------------------------------------------
my $strVagrantPath = "${strBackRestBase}/test/.vagrant";
my $strLibCPath = "${strVagrantPath}/libc";
my $strLibCSmart = "${strLibCPath}/build.timestamp";
my @stryLibCSrcPath = ('libc', 'src');
# VM Info
my $oVm = vmGet();
# Find the lastest modified time for dirs that affect the libc build
my $lTimestampLast = $oStorageBackRest->exists($strLibCSmart) ? $oStorageBackRest->info($strLibCSmart)->mtime : 0;
foreach my $strLibCSrcPath (@stryLibCSrcPath)
{
my $hManifest = $oStorageBackRest->manifest($strLibCSrcPath);
@@ -431,8 +504,8 @@ eval
}
# Loop through VMs to do the C Library builds
my $bLogDetail = $strLogLevel eq 'detail';
my @stryBuildVm = $strVm eq VM_ALL ? VM_LIST : ($strVm eq $strVmHost ? ($strVm) : ($strVm, $strVmHost));
$bLogDetail = $strLogLevel eq 'detail';
@stryBuildVm = $strVm eq VM_ALL ? VM_LIST : ($strVm eq $strVmHost ? ($strVm) : ($strVm, $strVmHost));
foreach my $strBuildVM (sort(@stryBuildVm))
{
@@ -441,7 +514,7 @@ eval
if (!$oStorageBackRest->pathExists($strBuildPath))
{
&log(INFO, "build/test C library for ${strBuildVM} (${strBuildPath})");
&log(INFO, "build C library for ${strBuildVM} (${strBuildPath})");
if ($bContainerExists)
{
@@ -518,7 +591,7 @@ eval
#---------------------------------------------------------------------------------------------------------------------------
my $strPackagePath = "${strVagrantPath}/package";
my $strPackageSmart = "${strPackagePath}/build.timestamp";
my @stryPackageSrcPath = ('bin', 'lib');
my @stryPackageSrcPath = ('lib');
# Find the lastest modified time for additional dirs that affect the package build
foreach my $strPackageSrcPath (@stryPackageSrcPath)
@@ -574,6 +647,21 @@ eval
"bash -c 'cp -r /root/package-src/debian ${strBuildPath}' && sudo chown -R " . TEST_USER .
" ${strBuildPath}");
# Patch files in debian package builds
#
# Use these commands to create a new patch (may need to modify first line):
# BRDIR=/backrest;BRVM=u16;BRPATCHFILE=${BRDIR?}/test/patch/debian-package.patch
# DBDIR=${BRDIR?}/test/.vagrant/package/${BRVM}/src/debian
# diff -Naur ${DBDIR?}.old ${DBDIR}.new > ${BRPATCHFILE?}
my $strDebianPackagePatch = "${strBackRestBase}/test/patch/debian-package.patch";
if ($oStorageBackRest->exists($strDebianPackagePatch))
{
executeTest("cp -r ${strBuildPath}/debian ${strBuildPath}/debian.old");
executeTest("patch -d ${strBuildPath}/debian < ${strDebianPackagePatch}");
executeTest("cp -r ${strBuildPath}/debian ${strBuildPath}/debian.new");
}
# If dev build then disable static release date used for reproducibility
if ($bVersionDev)
{
@@ -633,22 +721,12 @@ eval
{
my $strBasePath = dirname(dirname(abs_path($0)));
&log(INFO, "Performing static code analysis using perl -cw");
# Check the exe for warnings
my $strWarning = trim(executeTest("perl -cw ${strBasePath}/bin/pgbackrest 2>&1"));
if ($strWarning ne "${strBasePath}/bin/pgbackrest syntax OK")
{
confess &log(ERROR, "${strBasePath}/bin/pgbackrest failed syntax check:\n${strWarning}");
}
&log(INFO, "Performing static code analysis using perlcritic");
executeTest('perlcritic --quiet --verbose=8 --brutal --top=10' .
' --verbose "[%p] %f: %m at line %l, column %c. %e. (Severity: %s)\n"' .
" \"--profile=${strBasePath}/test/lint/perlcritic.policy\"" .
" ${strBasePath}/bin/pgbackrest ${strBasePath}/lib/*" .
" ${strBasePath}/lib/*" .
" ${strBasePath}/test/test.pl ${strBasePath}/test/lib/*" .
" ${strBasePath}/doc/doc.pl ${strBasePath}/doc/lib/*");
}
@@ -956,7 +1034,8 @@ eval
$strVm, $iVmId, # Vm info
$strBackRestBase, # Base backrest directory
$strTestPath, # Path where the tests will run
"${strBackRestBase}/bin/" . BACKREST_EXE, # Path to the backrest executable
'/usr/bin/' . BACKREST_EXE, # Path to the backrest executable
"${strBackRestBase}/bin/" . BACKREST_EXE, # Path to the backrest Perl helper
$strDbVersion ne 'minimal' ? $strPgSqlBin: undef, # Db bin path
$strDbVersion ne 'minimal' ? $strDbVersion: undef, # Db version
$stryModule[0], $stryModuleTest[0], \@iyModuleTestRun, # Module info