2014-12-15 22:20:42 +00:00
|
|
|
####################################################################################################################################
|
|
|
|
# CONFIG MODULE
|
|
|
|
####################################################################################################################################
|
2016-04-14 09:30:54 -04:00
|
|
|
package pgBackRest::Config::Config;
|
2014-12-15 22:20:42 +00:00
|
|
|
|
|
|
|
use strict;
|
2015-03-03 00:57:20 -05:00
|
|
|
use warnings FATAL => qw(all);
|
|
|
|
use Carp qw(confess);
|
2018-01-28 21:37:09 -05:00
|
|
|
use English '-no_match_vars';
|
2014-12-15 22:20:42 +00:00
|
|
|
|
2015-03-12 12:15:19 -04:00
|
|
|
use Exporter qw(import);
|
2015-08-29 14:20:46 -04:00
|
|
|
our @EXPORT = qw();
|
2018-01-28 21:37:09 -05:00
|
|
|
use JSON::PP;
|
2014-12-15 22:20:42 +00:00
|
|
|
|
2016-04-14 09:30:54 -04:00
|
|
|
use pgBackRest::Common::Exception;
|
|
|
|
use pgBackRest::Common::Ini;
|
2017-06-09 17:51:41 -04:00
|
|
|
use pgBackRest::Common::Io::Base;
|
2016-04-14 09:30:54 -04:00
|
|
|
use pgBackRest::Common::Log;
|
2017-08-25 16:47:47 -04:00
|
|
|
use pgBackRest::Common::String;
|
2016-06-12 09:13:46 -04:00
|
|
|
use pgBackRest::Common::Wait;
|
2017-11-26 17:45:00 -05:00
|
|
|
use pgBackRest::LibC qw(:config :configDefine);
|
2016-04-14 09:30:54 -04:00
|
|
|
use pgBackRest::Version;
|
2015-07-02 10:05:13 -04:00
|
|
|
|
2015-03-12 12:15:19 -04:00
|
|
|
####################################################################################################################################
|
2017-08-25 16:47:47 -04:00
|
|
|
# Export config constants and functions
|
2015-03-12 12:15:19 -04:00
|
|
|
####################################################################################################################################
|
2017-11-26 17:45:00 -05:00
|
|
|
push(@EXPORT, @{$pgBackRest::LibC::EXPORT_TAGS{config}});
|
|
|
|
push(@EXPORT, @{$pgBackRest::LibC::EXPORT_TAGS{configDefine}});
|
2014-12-15 22:20:42 +00:00
|
|
|
|
2017-06-09 17:51:41 -04:00
|
|
|
####################################################################################################################################
|
2017-08-25 16:47:47 -04:00
|
|
|
# SOURCE Constants
|
|
|
|
####################################################################################################################################
|
|
|
|
use constant CFGDEF_SOURCE_CONFIG => 'config';
|
|
|
|
push @EXPORT, qw(CFGDEF_SOURCE_CONFIG);
|
|
|
|
use constant CFGDEF_SOURCE_PARAM => 'param';
|
|
|
|
push @EXPORT, qw(CFGDEF_SOURCE_PARAM);
|
|
|
|
use constant CFGDEF_SOURCE_DEFAULT => 'default';
|
|
|
|
push @EXPORT, qw(CFGDEF_SOURCE_DEFAULT);
|
2014-12-15 22:20:42 +00:00
|
|
|
|
2017-06-09 17:51:41 -04:00
|
|
|
####################################################################################################################################
|
2017-08-25 16:47:47 -04:00
|
|
|
# Configuration section constants
|
2017-06-09 17:51:41 -04:00
|
|
|
####################################################################################################################################
|
2017-08-25 16:47:47 -04:00
|
|
|
use constant CFGDEF_SECTION_GLOBAL => 'global';
|
|
|
|
push @EXPORT, qw(CFGDEF_SECTION_GLOBAL);
|
|
|
|
use constant CFGDEF_SECTION_STANZA => 'stanza';
|
|
|
|
push @EXPORT, qw(CFGDEF_SECTION_STANZA);
|
2017-06-09 17:51:41 -04:00
|
|
|
|
2015-03-12 12:15:19 -04:00
|
|
|
####################################################################################################################################
|
2016-08-24 12:39:27 -04:00
|
|
|
# Module variables
|
2015-03-12 12:15:19 -04:00
|
|
|
####################################################################################################################################
|
2015-07-02 10:05:13 -04:00
|
|
|
my %oOption; # Option hash
|
|
|
|
my $strCommand; # Command (backup, archive-get, ...)
|
2016-12-03 18:01:17 -05:00
|
|
|
my $bInitLog = false; # Has logging been initialized yet?
|
|
|
|
|
|
|
|
####################################################################################################################################
|
2017-08-25 16:47:47 -04:00
|
|
|
# configLogging - configure logging based on options
|
2016-12-03 18:01:17 -05:00
|
|
|
####################################################################################################################################
|
|
|
|
sub configLogging
|
|
|
|
{
|
|
|
|
my $bLogInitForce = shift;
|
|
|
|
|
2016-12-04 17:51:00 -05:00
|
|
|
if ($bInitLog || (defined($bLogInitForce) && $bLogInitForce))
|
2016-12-03 18:01:17 -05:00
|
|
|
{
|
|
|
|
logLevelSet(
|
2017-08-25 16:47:47 -04:00
|
|
|
cfgOptionValid(CFGOPT_LOG_LEVEL_FILE) ? cfgOption(CFGOPT_LOG_LEVEL_FILE) : OFF,
|
|
|
|
cfgOptionValid(CFGOPT_LOG_LEVEL_CONSOLE) ? cfgOption(CFGOPT_LOG_LEVEL_CONSOLE) : OFF,
|
|
|
|
cfgOptionValid(CFGOPT_LOG_LEVEL_STDERR) ? cfgOption(CFGOPT_LOG_LEVEL_STDERR) : OFF,
|
|
|
|
cfgOptionValid(CFGOPT_LOG_TIMESTAMP) ? cfgOption(CFGOPT_LOG_TIMESTAMP) : undef);
|
2016-12-03 18:01:17 -05:00
|
|
|
|
|
|
|
$bInitLog = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
push @EXPORT, qw(configLogging);
|
2014-12-15 22:20:42 +00:00
|
|
|
|
2015-01-31 15:37:59 -05:00
|
|
|
####################################################################################################################################
|
2017-08-25 16:47:47 -04:00
|
|
|
# configLoad - load configuration
|
2015-03-12 12:15:19 -04:00
|
|
|
#
|
2017-11-02 08:14:13 -04:00
|
|
|
# Additional conditions that cannot be codified by the option definitions are also tested here.
|
2015-01-31 15:37:59 -05:00
|
|
|
####################################################################################################################################
|
2015-03-12 12:15:19 -04:00
|
|
|
sub configLoad
|
2015-01-31 15:37:59 -05:00
|
|
|
{
|
2016-09-07 08:07:37 -04:00
|
|
|
my $bInitLogging = shift;
|
2018-01-28 21:37:09 -05:00
|
|
|
my $strBackRestBin = shift;
|
|
|
|
my $strCommandName = shift;
|
2018-03-08 16:24:16 -05:00
|
|
|
my $rstrConfigJson = shift;
|
2016-09-07 08:07:37 -04:00
|
|
|
|
2018-01-28 21:37:09 -05:00
|
|
|
# Set backrest bin
|
|
|
|
backrestBinSet($strBackRestBin);
|
2015-01-31 15:37:59 -05:00
|
|
|
|
2018-01-28 21:37:09 -05:00
|
|
|
# Set command
|
|
|
|
$strCommand = $strCommandName;
|
|
|
|
|
|
|
|
eval
|
|
|
|
{
|
2018-02-05 12:32:30 -05:00
|
|
|
# Hacky fix for backslashes that need to be escaped
|
2018-03-08 16:24:16 -05:00
|
|
|
$$rstrConfigJson =~ s/\\/\\\\/g;
|
2018-02-05 12:32:30 -05:00
|
|
|
|
2018-03-09 09:30:50 -05:00
|
|
|
%oOption = %{(JSON::PP->new()->allow_nonref())->decode($$rstrConfigJson)};
|
2018-02-05 12:32:30 -05:00
|
|
|
return true;
|
2018-01-28 21:37:09 -05:00
|
|
|
}
|
|
|
|
or do
|
|
|
|
{
|
2018-03-08 16:24:16 -05:00
|
|
|
confess &log(ASSERT, "unable to parse config JSON");
|
2018-01-28 21:37:09 -05:00
|
|
|
};
|
|
|
|
|
|
|
|
# Load options into final option hash
|
2017-08-25 16:47:47 -04:00
|
|
|
for (my $iOptionId = 0; $iOptionId < cfgOptionTotal(); $iOptionId++)
|
2015-01-31 15:37:59 -05:00
|
|
|
{
|
2018-01-28 21:37:09 -05:00
|
|
|
my $strOptionName = cfgOptionName($iOptionId);
|
2017-08-25 16:47:47 -04:00
|
|
|
|
2018-03-09 09:30:50 -05:00
|
|
|
# If option is defined it is valid
|
|
|
|
if (defined($oOption{$strOptionName}))
|
2018-01-28 21:37:09 -05:00
|
|
|
{
|
2018-03-09 09:30:50 -05:00
|
|
|
# Convert JSON bool to standard bool that Perl understands
|
|
|
|
if (cfgDefOptionType($iOptionId) eq CFGDEF_TYPE_BOOLEAN && defined($oOption{$strOptionName}{value}))
|
|
|
|
{
|
|
|
|
$oOption{$strOptionName}{value} = $oOption{$strOptionName}{value} eq INI_TRUE ? true : false;
|
|
|
|
}
|
2018-01-28 21:37:09 -05:00
|
|
|
}
|
2018-03-09 09:30:50 -05:00
|
|
|
# Else it is not valid
|
2018-01-28 21:37:09 -05:00
|
|
|
else
|
2015-03-12 12:15:19 -04:00
|
|
|
{
|
2018-03-09 09:30:50 -05:00
|
|
|
$oOption{$strOptionName}{valid} = false;
|
2015-03-12 12:15:19 -04:00
|
|
|
}
|
2015-01-31 15:37:59 -05:00
|
|
|
}
|
|
|
|
|
2016-09-07 08:07:37 -04:00
|
|
|
# If this is not the remote and logging is allowed (to not overwrite log levels for tests) then set the log level so that
|
2016-12-03 18:01:17 -05:00
|
|
|
# INFO/WARN messages can be displayed (the user may still disable them). This should be run before any WARN logging is
|
|
|
|
# generated.
|
|
|
|
if (!defined($bInitLogging) || $bInitLogging)
|
2016-09-07 08:07:37 -04:00
|
|
|
{
|
2016-12-03 18:01:17 -05:00
|
|
|
configLogging(true);
|
2016-09-07 08:07:37 -04:00
|
|
|
}
|
|
|
|
|
2017-08-25 16:47:47 -04:00
|
|
|
# Neutralize the umask to make the repository file/path modes more consistent
|
|
|
|
if (cfgOptionValid(CFGOPT_NEUTRAL_UMASK) && cfgOption(CFGOPT_NEUTRAL_UMASK))
|
2016-12-04 14:44:53 -05:00
|
|
|
{
|
2017-08-25 16:47:47 -04:00
|
|
|
umask(0000);
|
2016-12-04 14:44:53 -05:00
|
|
|
}
|
|
|
|
|
2016-06-22 18:01:18 -04:00
|
|
|
# Protocol timeout should be greater than db timeout
|
2017-08-25 16:47:47 -04:00
|
|
|
if (cfgOptionTest(CFGOPT_DB_TIMEOUT) && cfgOptionTest(CFGOPT_PROTOCOL_TIMEOUT) &&
|
|
|
|
cfgOption(CFGOPT_PROTOCOL_TIMEOUT) <= cfgOption(CFGOPT_DB_TIMEOUT))
|
2016-06-22 18:01:18 -04:00
|
|
|
{
|
2016-08-25 11:27:00 -04:00
|
|
|
# If protocol-timeout is default then increase it to be greater than db-timeout
|
2017-08-25 16:47:47 -04:00
|
|
|
if (cfgOptionSource(CFGOPT_PROTOCOL_TIMEOUT) eq CFGDEF_SOURCE_DEFAULT)
|
2016-08-25 11:27:00 -04:00
|
|
|
{
|
2017-08-25 16:47:47 -04:00
|
|
|
cfgOptionSet(CFGOPT_PROTOCOL_TIMEOUT, cfgOption(CFGOPT_DB_TIMEOUT) + 30);
|
2016-08-25 11:27:00 -04:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
confess &log(ERROR,
|
2017-08-25 16:47:47 -04:00
|
|
|
"'" . cfgOption(CFGOPT_PROTOCOL_TIMEOUT) . "' is not valid for '" .
|
|
|
|
cfgOptionName(CFGOPT_PROTOCOL_TIMEOUT) . "' option\n" .
|
2017-12-05 14:59:09 -05:00
|
|
|
"HINT: 'protocol-timeout' option (" . cfgOption(CFGOPT_PROTOCOL_TIMEOUT) .
|
|
|
|
") should be greater than 'db-timeout' option (" . cfgOption(CFGOPT_DB_TIMEOUT) . ").",
|
2016-08-25 11:27:00 -04:00
|
|
|
ERROR_OPTION_INVALID_VALUE);
|
|
|
|
}
|
2016-06-22 18:01:18 -04:00
|
|
|
}
|
|
|
|
|
2016-08-24 12:39:27 -04:00
|
|
|
# Make sure that backup and db are not both remote
|
2018-02-03 18:27:38 -05:00
|
|
|
if (cfgOptionTest(CFGOPT_PG_HOST) && cfgOptionTest(CFGOPT_REPO_HOST))
|
2015-04-01 15:58:33 -04:00
|
|
|
{
|
2016-08-24 12:39:27 -04:00
|
|
|
confess &log(ERROR, 'db and backup cannot both be configured as remote', ERROR_CONFIG);
|
2015-04-01 15:58:33 -04:00
|
|
|
}
|
|
|
|
|
2018-02-19 15:07:24 -05:00
|
|
|
# Warn when repo-retention-full is not set
|
|
|
|
if (cfgOptionValid(CFGOPT_REPO_RETENTION_FULL) && !cfgOptionTest(CFGOPT_REPO_RETENTION_FULL))
|
2016-09-07 08:07:37 -04:00
|
|
|
{
|
|
|
|
&log(WARN,
|
2018-02-19 15:07:24 -05:00
|
|
|
"option " . cfgOptionName(CFGOPT_REPO_RETENTION_FULL) . " is not set, the repository may run out of space\n" .
|
|
|
|
"HINT: to retain full backups indefinitely (without warning), set option '" .
|
|
|
|
cfgOptionName(CFGOPT_REPO_RETENTION_FULL) . "' to the maximum.");
|
2016-09-07 08:07:37 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
# If archive retention is valid for the command, then set archive settings
|
2018-02-19 15:07:24 -05:00
|
|
|
if (cfgOptionValid(CFGOPT_REPO_RETENTION_ARCHIVE))
|
2016-09-07 08:07:37 -04:00
|
|
|
{
|
2018-02-19 15:07:24 -05:00
|
|
|
my $strArchiveRetentionType = cfgOption(CFGOPT_REPO_RETENTION_ARCHIVE_TYPE, false);
|
|
|
|
my $iArchiveRetention = cfgOption(CFGOPT_REPO_RETENTION_ARCHIVE, false);
|
|
|
|
my $iFullRetention = cfgOption(CFGOPT_REPO_RETENTION_FULL, false);
|
|
|
|
my $iDifferentialRetention = cfgOption(CFGOPT_REPO_RETENTION_DIFF, false);
|
2016-09-07 08:07:37 -04:00
|
|
|
|
2018-02-19 15:07:24 -05:00
|
|
|
my $strMsgArchiveOff = "WAL segments will not be expired: option '" . cfgOptionName(CFGOPT_REPO_RETENTION_ARCHIVE_TYPE) .
|
2016-09-07 08:07:37 -04:00
|
|
|
"=${strArchiveRetentionType}' but ";
|
|
|
|
|
|
|
|
# If the archive retention is not explicitly set then determine what it should be set to so the user does not have to.
|
|
|
|
if (!defined($iArchiveRetention))
|
|
|
|
{
|
2018-02-19 15:07:24 -05:00
|
|
|
# If repo-retention-archive-type is default, then if repo-retention-full is set, set the repo-retention-archive to this
|
|
|
|
# value, else ignore archiving
|
2017-08-25 16:47:47 -04:00
|
|
|
if ($strArchiveRetentionType eq CFGOPTVAL_BACKUP_TYPE_FULL)
|
2016-09-07 08:07:37 -04:00
|
|
|
{
|
|
|
|
if (defined($iFullRetention))
|
|
|
|
{
|
2018-02-19 15:07:24 -05:00
|
|
|
cfgOptionSet(CFGOPT_REPO_RETENTION_ARCHIVE, $iFullRetention);
|
2016-09-07 08:07:37 -04:00
|
|
|
}
|
|
|
|
}
|
2017-08-25 16:47:47 -04:00
|
|
|
elsif ($strArchiveRetentionType eq CFGOPTVAL_BACKUP_TYPE_DIFF)
|
2016-09-07 08:07:37 -04:00
|
|
|
{
|
2018-02-19 15:07:24 -05:00
|
|
|
# if repo-retention-diff is set then user must have set it
|
2016-09-07 08:07:37 -04:00
|
|
|
if (defined($iDifferentialRetention))
|
|
|
|
{
|
2018-02-19 15:07:24 -05:00
|
|
|
cfgOptionSet(CFGOPT_REPO_RETENTION_ARCHIVE, $iDifferentialRetention);
|
2016-09-07 08:07:37 -04:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
&log(WARN,
|
2018-02-19 15:07:24 -05:00
|
|
|
$strMsgArchiveOff . "neither option '" . cfgOptionName(CFGOPT_REPO_RETENTION_ARCHIVE) .
|
|
|
|
"' nor option '" . cfgOptionName(CFGOPT_REPO_RETENTION_DIFF) . "' is set");
|
2016-09-07 08:07:37 -04:00
|
|
|
}
|
|
|
|
}
|
2017-08-25 16:47:47 -04:00
|
|
|
elsif ($strArchiveRetentionType eq CFGOPTVAL_BACKUP_TYPE_INCR)
|
2016-09-07 08:07:37 -04:00
|
|
|
{
|
2018-02-19 15:07:24 -05:00
|
|
|
&log(WARN, $strMsgArchiveOff . "option '" . cfgOptionName(CFGOPT_REPO_RETENTION_ARCHIVE) . "' is not set");
|
2016-09-07 08:07:37 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2018-02-19 15:07:24 -05:00
|
|
|
# If repo-retention-archive is set then check repo-retention-archive-type and issue a warning if the corresponding
|
|
|
|
# setting is UNDEF since UNDEF means backups will not be expired but they should be in the practice of setting this
|
2016-09-07 08:07:37 -04:00
|
|
|
# value even though expiring the archive itself is OK and will be performed.
|
2017-08-25 16:47:47 -04:00
|
|
|
if ($strArchiveRetentionType eq CFGOPTVAL_BACKUP_TYPE_DIFF && !defined($iDifferentialRetention))
|
2016-09-07 08:07:37 -04:00
|
|
|
{
|
|
|
|
&log(WARN,
|
2018-02-19 15:07:24 -05:00
|
|
|
"option '" . cfgOptionName(CFGOPT_REPO_RETENTION_DIFF) . "' is not set for '" .
|
|
|
|
cfgOptionName(CFGOPT_REPO_RETENTION_ARCHIVE_TYPE) . "=" . &CFGOPTVAL_BACKUP_TYPE_DIFF . "' \n" .
|
2016-09-07 08:07:37 -04:00
|
|
|
"HINT: to retain differential backups indefinitely (without warning), set option '" .
|
2018-02-19 15:07:24 -05:00
|
|
|
cfgOptionName(CFGOPT_REPO_RETENTION_DIFF) . "' to the maximum.");
|
2016-09-07 08:07:37 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-08-24 12:39:27 -04:00
|
|
|
return true;
|
|
|
|
}
|
2015-09-08 07:31:24 -04:00
|
|
|
|
2016-08-24 12:39:27 -04:00
|
|
|
push @EXPORT, qw(configLoad);
|
|
|
|
|
|
|
|
####################################################################################################################################
|
2018-02-03 18:27:38 -05:00
|
|
|
# cfgOptionIdFromIndex - return name for options that can be indexed (e.g. pg1-host, pg2-host).
|
2016-08-24 12:39:27 -04:00
|
|
|
####################################################################################################################################
|
2017-11-02 08:14:13 -04:00
|
|
|
sub cfgOptionIdFromIndex
|
2016-08-24 12:39:27 -04:00
|
|
|
{
|
2017-08-25 16:47:47 -04:00
|
|
|
my $iOptionId = shift;
|
2016-08-24 12:39:27 -04:00
|
|
|
my $iIndex = shift;
|
|
|
|
|
|
|
|
# If the option doesn't have a prefix it can't be indexed
|
|
|
|
$iIndex = defined($iIndex) ? $iIndex : 1;
|
2017-11-02 08:14:13 -04:00
|
|
|
my $strPrefix = cfgDefOptionPrefix($iOptionId);
|
2016-08-24 12:39:27 -04:00
|
|
|
|
2017-08-25 16:47:47 -04:00
|
|
|
if (!defined($strPrefix))
|
2016-08-24 12:39:27 -04:00
|
|
|
{
|
2017-08-25 16:47:47 -04:00
|
|
|
if ($iIndex > 1)
|
|
|
|
{
|
|
|
|
confess &log(ASSERT, "'" . cfgOptionName($iOptionId) . "' option does not allow indexing");
|
|
|
|
}
|
2016-08-24 12:39:27 -04:00
|
|
|
|
2017-08-25 16:47:47 -04:00
|
|
|
return $iOptionId;
|
2016-08-24 12:39:27 -04:00
|
|
|
}
|
|
|
|
|
2017-08-25 16:47:47 -04:00
|
|
|
return cfgOptionId("${strPrefix}${iIndex}" . substr(cfgOptionName($iOptionId), index(cfgOptionName($iOptionId), '-')));
|
2016-08-24 12:39:27 -04:00
|
|
|
}
|
|
|
|
|
2017-11-02 08:14:13 -04:00
|
|
|
push @EXPORT, qw(cfgOptionIdFromIndex);
|
2016-08-24 12:39:27 -04:00
|
|
|
|
2015-03-16 14:01:01 -04:00
|
|
|
####################################################################################################################################
|
2017-08-25 16:47:47 -04:00
|
|
|
# cfgOptionSource - how was the option set?
|
2015-03-16 14:01:01 -04:00
|
|
|
####################################################################################################################################
|
2017-08-25 16:47:47 -04:00
|
|
|
sub cfgOptionSource
|
2015-03-16 14:01:01 -04:00
|
|
|
{
|
2017-08-25 16:47:47 -04:00
|
|
|
my $iOptionId = shift;
|
2015-03-16 14:01:01 -04:00
|
|
|
|
2017-08-25 16:47:47 -04:00
|
|
|
cfgOptionValid($iOptionId, true);
|
2015-03-16 14:01:01 -04:00
|
|
|
|
2017-08-25 16:47:47 -04:00
|
|
|
return $oOption{cfgOptionName($iOptionId)}{source};
|
2015-03-16 14:01:01 -04:00
|
|
|
}
|
|
|
|
|
2017-08-25 16:47:47 -04:00
|
|
|
push @EXPORT, qw(cfgOptionSource);
|
2015-03-12 12:15:19 -04:00
|
|
|
|
2015-10-28 10:10:36 +01:00
|
|
|
####################################################################################################################################
|
2017-08-25 16:47:47 -04:00
|
|
|
# cfgOptionValid - is the option valid for the current command?
|
2015-10-28 10:10:36 +01:00
|
|
|
####################################################################################################################################
|
2017-08-25 16:47:47 -04:00
|
|
|
sub cfgOptionValid
|
2015-10-28 10:10:36 +01:00
|
|
|
{
|
2017-08-25 16:47:47 -04:00
|
|
|
my $iOptionId = shift;
|
|
|
|
my $bError = shift;
|
2015-10-28 10:10:36 +01:00
|
|
|
|
2017-08-25 16:47:47 -04:00
|
|
|
# If defined then this is the command help is being generated for so all valid checks should be against that command
|
|
|
|
my $iCommandId;
|
2015-10-28 10:10:36 +01:00
|
|
|
|
2018-01-23 13:34:24 -05:00
|
|
|
if (defined($strCommand))
|
2017-08-25 16:47:47 -04:00
|
|
|
{
|
|
|
|
$iCommandId = cfgCommandId($strCommand);
|
2015-10-28 10:10:36 +01:00
|
|
|
}
|
|
|
|
|
2017-11-02 08:14:13 -04:00
|
|
|
if (defined($iCommandId) && cfgDefOptionValid($iCommandId, $iOptionId))
|
2016-06-18 09:55:00 -04:00
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (defined($bError) && $bError)
|
|
|
|
{
|
2017-08-25 16:47:47 -04:00
|
|
|
my $strOption = cfgOptionName($iOptionId);
|
|
|
|
|
2016-06-18 09:55:00 -04:00
|
|
|
if (!defined($oOption{$strOption}))
|
|
|
|
{
|
|
|
|
confess &log(ASSERT, "option '${strOption}' does not exist");
|
|
|
|
}
|
|
|
|
|
2017-08-25 16:47:47 -04:00
|
|
|
confess &log(ASSERT, "option '${strOption}' not valid for command '" . cfgCommandName(cfgCommandGet()) . "'");
|
2016-06-18 09:55:00 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-08-25 16:47:47 -04:00
|
|
|
push @EXPORT, qw(cfgOptionValid);
|
2016-06-18 09:55:00 -04:00
|
|
|
|
2015-03-12 12:15:19 -04:00
|
|
|
####################################################################################################################################
|
2017-08-25 16:47:47 -04:00
|
|
|
# cfgOption - get option value
|
2015-03-12 12:15:19 -04:00
|
|
|
####################################################################################################################################
|
2017-08-25 16:47:47 -04:00
|
|
|
sub cfgOption
|
2015-03-12 12:15:19 -04:00
|
|
|
{
|
2017-08-25 16:47:47 -04:00
|
|
|
my $iOptionId = shift;
|
2015-03-12 12:15:19 -04:00
|
|
|
my $bRequired = shift;
|
|
|
|
|
2017-08-25 16:47:47 -04:00
|
|
|
cfgOptionValid($iOptionId, true);
|
|
|
|
|
|
|
|
my $strOption = cfgOptionName($iOptionId);
|
2016-06-18 09:55:00 -04:00
|
|
|
|
2015-04-19 17:29:52 -04:00
|
|
|
if (!defined($oOption{$strOption}{value}) && (!defined($bRequired) || $bRequired))
|
2015-03-02 20:36:12 -05:00
|
|
|
{
|
2015-03-12 12:15:19 -04:00
|
|
|
confess &log(ASSERT, "option ${strOption} is required");
|
2015-03-02 20:36:12 -05:00
|
|
|
}
|
|
|
|
|
2015-04-19 17:29:52 -04:00
|
|
|
return $oOption{$strOption}{value};
|
|
|
|
}
|
|
|
|
|
2017-08-25 16:47:47 -04:00
|
|
|
push @EXPORT, qw(cfgOption);
|
2015-03-02 22:58:32 -05:00
|
|
|
|
2016-02-05 23:03:29 -05:00
|
|
|
####################################################################################################################################
|
2017-08-25 16:47:47 -04:00
|
|
|
# cfgOptionDefault - get option default value
|
2016-02-05 23:03:29 -05:00
|
|
|
####################################################################################################################################
|
2017-08-25 16:47:47 -04:00
|
|
|
sub cfgOptionDefault
|
2016-02-05 23:03:29 -05:00
|
|
|
{
|
2017-08-25 16:47:47 -04:00
|
|
|
my $iOptionId = shift;
|
|
|
|
|
|
|
|
cfgOptionValid($iOptionId, true);
|
|
|
|
|
2017-11-02 08:14:13 -04:00
|
|
|
return cfgDefOptionDefault(cfgCommandId($strCommand), $iOptionId);
|
2017-08-25 16:47:47 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
push @EXPORT, qw(cfgOptionDefault);
|
|
|
|
|
|
|
|
####################################################################################################################################
|
|
|
|
# cfgOptionSet - set option value and source
|
|
|
|
####################################################################################################################################
|
|
|
|
sub cfgOptionSet
|
|
|
|
{
|
|
|
|
my $iOptionId = shift;
|
2016-02-05 23:03:29 -05:00
|
|
|
my $oValue = shift;
|
2016-12-04 17:51:00 -05:00
|
|
|
my $bForce = shift;
|
2016-02-05 23:03:29 -05:00
|
|
|
|
2017-08-25 16:47:47 -04:00
|
|
|
my $strOption = cfgOptionName($iOptionId);
|
|
|
|
|
|
|
|
if (!cfgOptionValid($iOptionId, !defined($bForce) || !$bForce))
|
2016-12-04 17:51:00 -05:00
|
|
|
{
|
|
|
|
$oOption{$strOption}{valid} = true;
|
|
|
|
}
|
2016-06-18 09:55:00 -04:00
|
|
|
|
2017-08-25 16:47:47 -04:00
|
|
|
$oOption{$strOption}{source} = CFGDEF_SOURCE_PARAM;
|
2016-02-05 23:03:29 -05:00
|
|
|
$oOption{$strOption}{value} = $oValue;
|
|
|
|
}
|
|
|
|
|
2017-08-25 16:47:47 -04:00
|
|
|
push @EXPORT, qw(cfgOptionSet);
|
2016-02-05 23:03:29 -05:00
|
|
|
|
2015-03-12 12:15:19 -04:00
|
|
|
####################################################################################################################################
|
2017-08-25 16:47:47 -04:00
|
|
|
# cfgOptionTest - test if an option exists or has a specific value
|
2015-03-12 12:15:19 -04:00
|
|
|
####################################################################################################################################
|
2017-08-25 16:47:47 -04:00
|
|
|
sub cfgOptionTest
|
2015-03-12 12:15:19 -04:00
|
|
|
{
|
2017-08-25 16:47:47 -04:00
|
|
|
my $iOptionId = shift;
|
2015-03-12 12:15:19 -04:00
|
|
|
my $strValue = shift;
|
2015-01-31 15:37:59 -05:00
|
|
|
|
2017-08-25 16:47:47 -04:00
|
|
|
if (!cfgOptionValid($iOptionId))
|
2016-06-18 09:55:00 -04:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2015-03-12 12:15:19 -04:00
|
|
|
if (defined($strValue))
|
2015-01-31 15:37:59 -05:00
|
|
|
{
|
2017-08-25 16:47:47 -04:00
|
|
|
return cfgOption($iOptionId) eq $strValue ? true : false;
|
2015-01-31 15:37:59 -05:00
|
|
|
}
|
2015-03-12 12:15:19 -04:00
|
|
|
|
2017-08-25 16:47:47 -04:00
|
|
|
return defined($oOption{cfgOptionName($iOptionId)}{value}) ? true : false;
|
2015-03-12 12:15:19 -04:00
|
|
|
}
|
|
|
|
|
2017-08-25 16:47:47 -04:00
|
|
|
push @EXPORT, qw(cfgOptionTest);
|
2015-08-29 14:20:46 -04:00
|
|
|
|
|
|
|
####################################################################################################################################
|
2017-08-25 16:47:47 -04:00
|
|
|
# cfgCommandGet - get the current command
|
2015-04-01 15:58:33 -04:00
|
|
|
####################################################################################################################################
|
2017-08-25 16:47:47 -04:00
|
|
|
sub cfgCommandGet
|
2015-04-01 15:58:33 -04:00
|
|
|
{
|
2017-08-25 16:47:47 -04:00
|
|
|
return cfgCommandId($strCommand);
|
2015-04-01 15:58:33 -04:00
|
|
|
}
|
|
|
|
|
2017-08-25 16:47:47 -04:00
|
|
|
push @EXPORT, qw(cfgCommandGet);
|
2015-08-29 14:20:46 -04:00
|
|
|
|
2015-03-12 12:15:19 -04:00
|
|
|
####################################################################################################################################
|
2017-08-25 16:47:47 -04:00
|
|
|
# cfgCommandTest - test that the current command is equal to the provided value
|
2015-03-12 12:15:19 -04:00
|
|
|
####################################################################################################################################
|
2017-08-25 16:47:47 -04:00
|
|
|
sub cfgCommandTest
|
2015-03-12 12:15:19 -04:00
|
|
|
{
|
2017-08-25 16:47:47 -04:00
|
|
|
my $iCommandIdTest = shift;
|
2015-08-29 14:20:46 -04:00
|
|
|
|
2017-08-25 16:47:47 -04:00
|
|
|
return cfgCommandName($iCommandIdTest) eq $strCommand;
|
2015-08-29 14:20:46 -04:00
|
|
|
}
|
|
|
|
|
2017-08-25 16:47:47 -04:00
|
|
|
push @EXPORT, qw(cfgCommandTest);
|
2015-08-29 14:20:46 -04:00
|
|
|
|
|
|
|
####################################################################################################################################
|
2016-12-04 14:44:53 -05:00
|
|
|
# commandBegin
|
2015-08-29 14:20:46 -04:00
|
|
|
#
|
2016-12-04 14:44:53 -05:00
|
|
|
# Log information about the command when it begins.
|
2015-08-29 14:20:46 -04:00
|
|
|
####################################################################################################################################
|
2016-12-04 14:44:53 -05:00
|
|
|
sub commandBegin
|
2015-08-29 14:20:46 -04:00
|
|
|
{
|
2016-09-17 11:53:28 -04:00
|
|
|
&log(
|
2017-08-25 16:47:47 -04:00
|
|
|
$strCommand eq cfgCommandName(CFGCMD_INFO) ? DEBUG : INFO,
|
2017-10-24 12:35:36 -04:00
|
|
|
"${strCommand} command begin " . BACKREST_VERSION . ':' .
|
|
|
|
cfgCommandWrite(cfgCommandId($strCommand), true, '', false, undef, true));
|
2015-08-29 14:20:46 -04:00
|
|
|
}
|
|
|
|
|
2018-03-12 10:55:58 -04:00
|
|
|
push @EXPORT, qw(commandBegin);
|
|
|
|
|
2015-08-29 14:20:46 -04:00
|
|
|
####################################################################################################################################
|
2016-12-04 14:44:53 -05:00
|
|
|
# commandEnd
|
2015-08-29 14:20:46 -04:00
|
|
|
#
|
2016-12-04 14:44:53 -05:00
|
|
|
# Log information about the command that ended.
|
2015-08-29 14:20:46 -04:00
|
|
|
####################################################################################################################################
|
2016-12-04 14:44:53 -05:00
|
|
|
sub commandEnd
|
2015-08-29 14:20:46 -04:00
|
|
|
{
|
2016-12-04 14:44:53 -05:00
|
|
|
my $iExitCode = shift;
|
|
|
|
my $strSignal = shift;
|
|
|
|
|
2015-09-08 07:31:24 -04:00
|
|
|
if (defined($strCommand))
|
|
|
|
{
|
2016-12-04 14:44:53 -05:00
|
|
|
&log(
|
2017-08-25 16:47:47 -04:00
|
|
|
$strCommand eq cfgCommandName(CFGCMD_INFO) ? DEBUG : INFO,
|
2016-12-04 14:44:53 -05:00
|
|
|
"${strCommand} command end: " . (defined($iExitCode) && $iExitCode != 0 ?
|
|
|
|
($iExitCode == ERROR_TERM ? "terminated on signal " .
|
|
|
|
(defined($strSignal) ? "[SIG${strSignal}]" : 'from child process') :
|
2017-04-12 12:41:34 -04:00
|
|
|
sprintf('aborted with exception [%03d]', $iExitCode)) :
|
2016-12-04 14:44:53 -05:00
|
|
|
'completed successfully'));
|
2015-09-08 07:31:24 -04:00
|
|
|
}
|
2015-08-29 14:20:46 -04:00
|
|
|
}
|
|
|
|
|
2016-12-04 14:44:53 -05:00
|
|
|
push @EXPORT, qw(commandEnd);
|
2015-08-29 14:20:46 -04:00
|
|
|
|
|
|
|
####################################################################################################################################
|
2017-08-25 16:47:47 -04:00
|
|
|
# cfgCommandSet - set current command (usually for triggering follow-on commands)
|
2015-08-29 14:20:46 -04:00
|
|
|
####################################################################################################################################
|
2017-08-25 16:47:47 -04:00
|
|
|
sub cfgCommandSet
|
2015-08-29 14:20:46 -04:00
|
|
|
{
|
2017-08-25 16:47:47 -04:00
|
|
|
my $iCommandId = shift;
|
2015-08-29 14:20:46 -04:00
|
|
|
|
2016-12-04 14:44:53 -05:00
|
|
|
commandEnd();
|
2015-08-29 14:20:46 -04:00
|
|
|
|
2017-08-25 16:47:47 -04:00
|
|
|
$strCommand = cfgCommandName($iCommandId);
|
2015-08-29 14:20:46 -04:00
|
|
|
|
2016-12-04 14:44:53 -05:00
|
|
|
commandBegin();
|
2015-08-29 14:20:46 -04:00
|
|
|
}
|
|
|
|
|
2017-08-25 16:47:47 -04:00
|
|
|
push @EXPORT, qw(cfgCommandSet);
|
2015-08-29 14:20:46 -04:00
|
|
|
|
|
|
|
####################################################################################################################################
|
2017-08-25 16:47:47 -04:00
|
|
|
# cfgCommandWrite - using the options for the current command, write the command string for another command
|
2015-08-29 14:20:46 -04:00
|
|
|
#
|
2017-08-25 16:47:47 -04:00
|
|
|
# For example, this can be used to write the archive-get command for recovery.conf during a restore.
|
2015-08-29 14:20:46 -04:00
|
|
|
####################################################################################################################################
|
2017-08-25 16:47:47 -04:00
|
|
|
sub cfgCommandWrite
|
2015-08-29 14:20:46 -04:00
|
|
|
{
|
2017-08-25 16:47:47 -04:00
|
|
|
my $iNewCommandId = shift;
|
2015-08-29 14:20:46 -04:00
|
|
|
my $bIncludeConfig = shift;
|
|
|
|
my $strExeString = shift;
|
|
|
|
my $bIncludeCommand = shift;
|
2015-10-08 11:43:56 -04:00
|
|
|
my $oOptionOverride = shift;
|
2017-10-24 12:35:36 -04:00
|
|
|
my $bDisplayOnly = shift;
|
2015-08-29 14:20:46 -04:00
|
|
|
|
|
|
|
# Set defaults
|
2017-11-26 18:43:51 -05:00
|
|
|
$strExeString = defined($strExeString) ? $strExeString : backrestBin();
|
2015-08-29 14:20:46 -04:00
|
|
|
$bIncludeConfig = defined($bIncludeConfig) ? $bIncludeConfig : false;
|
|
|
|
$bIncludeCommand = defined($bIncludeCommand) ? $bIncludeCommand : true;
|
|
|
|
|
2015-10-08 11:43:56 -04:00
|
|
|
# Iterate the options to figure out which ones are not default and need to be written out to the new command string
|
2017-08-25 16:47:47 -04:00
|
|
|
for (my $iOptionId = 0; $iOptionId < cfgOptionTotal(); $iOptionId++)
|
2015-08-29 14:20:46 -04:00
|
|
|
{
|
2017-08-25 16:47:47 -04:00
|
|
|
my $strOption = cfgOptionName($iOptionId);
|
2017-11-02 08:14:13 -04:00
|
|
|
my $bSecure = cfgDefOptionSecure($iOptionId);
|
2017-08-25 16:47:47 -04:00
|
|
|
|
2017-06-09 17:51:41 -04:00
|
|
|
# Skip option if it is secure and should not be output in logs or the command line
|
2017-10-24 12:35:36 -04:00
|
|
|
next if ($bSecure && !$bDisplayOnly);
|
2015-08-29 14:20:46 -04:00
|
|
|
|
2017-08-25 16:47:47 -04:00
|
|
|
# Process any option id overrides first
|
|
|
|
if (defined($oOptionOverride->{$iOptionId}))
|
|
|
|
{
|
|
|
|
if (defined($oOptionOverride->{$iOptionId}{value}))
|
|
|
|
{
|
2017-10-24 12:35:36 -04:00
|
|
|
$strExeString .= cfgCommandWriteOptionFormat(
|
|
|
|
$strOption, false, $bSecure, {value => $oOptionOverride->{$iOptionId}{value}});
|
2017-08-25 16:47:47 -04:00
|
|
|
}
|
|
|
|
}
|
2017-10-24 12:35:36 -04:00
|
|
|
# And process overrides passed by string - this is used by Perl compatibility functions
|
2017-08-25 16:47:47 -04:00
|
|
|
elsif (defined($oOptionOverride->{$strOption}))
|
2015-10-08 11:43:56 -04:00
|
|
|
{
|
2017-08-25 16:47:47 -04:00
|
|
|
if (defined($oOptionOverride->{$strOption}{value}))
|
2015-10-08 11:43:56 -04:00
|
|
|
{
|
2017-10-24 12:35:36 -04:00
|
|
|
$strExeString .= cfgCommandWriteOptionFormat(
|
|
|
|
$strOption, false, $bSecure, {value => $oOptionOverride->{$strOption}{value}});
|
2015-10-08 11:43:56 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
# else look for non-default options in the current configuration
|
2017-11-02 08:14:13 -04:00
|
|
|
elsif (cfgDefOptionValid($iNewCommandId, $iOptionId) &&
|
2015-10-08 11:43:56 -04:00
|
|
|
defined($oOption{$strOption}{value}) &&
|
2017-08-25 16:47:47 -04:00
|
|
|
($bIncludeConfig ?
|
|
|
|
$oOption{$strOption}{source} ne CFGDEF_SOURCE_DEFAULT : $oOption{$strOption}{source} eq CFGDEF_SOURCE_PARAM))
|
2015-08-29 14:20:46 -04:00
|
|
|
{
|
|
|
|
my $oValue;
|
2015-10-08 11:43:56 -04:00
|
|
|
my $bMulti = false;
|
2015-08-29 14:20:46 -04:00
|
|
|
|
|
|
|
# If this is a hash then it will break up into multple command-line options
|
|
|
|
if (ref($oOption{$strOption}{value}) eq 'HASH')
|
|
|
|
{
|
|
|
|
$oValue = $oOption{$strOption}{value};
|
2015-10-08 11:43:56 -04:00
|
|
|
$bMulti = true;
|
2015-08-29 14:20:46 -04:00
|
|
|
}
|
|
|
|
# Else a single value but store it in a hash anyway to make processing below simpler
|
|
|
|
else
|
|
|
|
{
|
|
|
|
$oValue = {value => $oOption{$strOption}{value}};
|
|
|
|
}
|
|
|
|
|
2017-10-24 12:35:36 -04:00
|
|
|
$strExeString .= cfgCommandWriteOptionFormat($strOption, $bMulti, $bSecure, $oValue);
|
2015-08-29 14:20:46 -04:00
|
|
|
}
|
2018-02-06 11:26:06 -05:00
|
|
|
# Else is reset
|
|
|
|
elsif (cfgDefOptionValid($iNewCommandId, $iOptionId) && $oOption{$strOption}{reset})
|
2017-12-05 15:23:32 -05:00
|
|
|
{
|
2018-02-06 11:26:06 -05:00
|
|
|
$strExeString .= " --reset-${strOption}";
|
2017-12-05 15:23:32 -05:00
|
|
|
}
|
2015-08-29 14:20:46 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
if ($bIncludeCommand)
|
|
|
|
{
|
2017-08-25 16:47:47 -04:00
|
|
|
$strExeString .= ' ' . cfgCommandName($iNewCommandId);
|
2015-08-29 14:20:46 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
return $strExeString;
|
2015-01-31 13:48:09 -05:00
|
|
|
}
|
|
|
|
|
2017-08-25 16:47:47 -04:00
|
|
|
push @EXPORT, qw(cfgCommandWrite);
|
2015-08-29 14:20:46 -04:00
|
|
|
|
2017-08-25 16:47:47 -04:00
|
|
|
# Helper function for cfgCommandWrite() to correctly format options for command-line usage
|
|
|
|
sub cfgCommandWriteOptionFormat
|
2016-02-23 09:25:22 -05:00
|
|
|
{
|
|
|
|
my $strOption = shift;
|
|
|
|
my $bMulti = shift;
|
2017-10-24 12:35:36 -04:00
|
|
|
my $bSecure = shift;
|
2016-02-23 09:25:22 -05:00
|
|
|
my $oValue = shift;
|
|
|
|
|
|
|
|
# Loops though all keys in the hash
|
|
|
|
my $strOptionFormat = '';
|
|
|
|
my $strParam;
|
|
|
|
|
|
|
|
foreach my $strKey (sort(keys(%$oValue)))
|
|
|
|
{
|
|
|
|
# Get the value - if the original value was a hash then the key must be prefixed
|
2017-10-24 12:35:36 -04:00
|
|
|
my $strValue = $bSecure ? '<redacted>' : ($bMulti ? "${strKey}=" : '') . $$oValue{$strKey};
|
2016-02-23 09:25:22 -05:00
|
|
|
|
|
|
|
# Handle the no- prefix for boolean values
|
2017-11-02 08:14:13 -04:00
|
|
|
if (cfgDefOptionType(cfgOptionId($strOption)) eq CFGDEF_TYPE_BOOLEAN)
|
2016-02-23 09:25:22 -05:00
|
|
|
{
|
|
|
|
$strParam = '--' . ($strValue ? '' : 'no-') . $strOption;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
$strParam = "--${strOption}=${strValue}";
|
|
|
|
}
|
|
|
|
|
|
|
|
# Add quotes if the value has spaces in it
|
|
|
|
$strOptionFormat .= ' ' . (index($strValue, " ") != -1 ? "\"${strParam}\"" : $strParam);
|
|
|
|
}
|
|
|
|
|
|
|
|
return $strOptionFormat;
|
|
|
|
}
|
|
|
|
|
2014-12-15 22:20:42 +00:00
|
|
|
1;
|