You've already forked pgbackrest
mirror of
https://github.com/pgbackrest/pgbackrest.git
synced 2025-07-15 01:04:37 +02:00
Config parsing implemented in C.
This commit is contained in:
@ -138,6 +138,6 @@ Subsequent code that is uncoverable for the same reason is marked with `// {+unc
|
||||
|
||||
Marks code that is not tested for one reason or another. This should be kept to a minimum and an excuse given for each instance.
|
||||
```
|
||||
exit(EXIT_FAILURE); // {uncoverable - test harness does not support non-zero exit}
|
||||
exit(EXIT_FAILURE); // {uncovered - test harness does not support non-zero exit}
|
||||
```
|
||||
Subsequent code that is uncovered for the same reason is marked with `// {+uncovered}`.
|
||||
|
@ -146,6 +146,7 @@ sub buildConfig
|
||||
|
||||
# Add "none" command that is used to initialize the current command before anything is parsed
|
||||
push(@{$rhEnum->{&BLD_LIST}}, buildConfigCommandEnum('none'));
|
||||
$iCommandTotal++;
|
||||
|
||||
$strBuildSource .=
|
||||
")\n";
|
||||
|
@ -108,15 +108,6 @@ sub buildConfigParse
|
||||
}
|
||||
}
|
||||
|
||||
# Perl options passed to the C binary should be ignored (unless calling Perl which is done elsewhere)
|
||||
$strBuildSource .=
|
||||
" // Perl option is ignored by normal config parsing\n" .
|
||||
" {\n" .
|
||||
" .name = \"perl-option\",\n" .
|
||||
" .has_arg = required_argument,\n" .
|
||||
" .val = 0,\n" .
|
||||
" },\n";
|
||||
|
||||
# The option list needs to be terminated or getopt_long will just keep on reading
|
||||
$strBuildSource .=
|
||||
" // Terminate option list\n" .
|
||||
|
@ -182,6 +182,10 @@ use constant CFGOPT_LOG_PATH => 'log-path
|
||||
use constant CFGOPT_SPOOL_PATH => 'spool-path';
|
||||
push @EXPORT, qw(CFGOPT_SPOOL_PATH);
|
||||
|
||||
# Perl
|
||||
use constant CFGOPT_PERL_OPTION => 'perl-option';
|
||||
push @EXPORT, qw(CFGOPT_PERL_OPTION);
|
||||
|
||||
# Repository
|
||||
use constant CFGOPT_REPO_PATH => 'repo-path';
|
||||
push @EXPORT, qw(CFGOPT_REPO_PATH);
|
||||
@ -1050,6 +1054,30 @@ my %hConfigDefine =
|
||||
},
|
||||
},
|
||||
|
||||
&CFGOPT_PERL_OPTION =>
|
||||
{
|
||||
&CFGDEF_SECTION => CFGDEF_SECTION_GLOBAL,
|
||||
&CFGDEF_TYPE => CFGDEF_TYPE_LIST,
|
||||
&CFGDEF_REQUIRED => false,
|
||||
&CFGDEF_INTERNAL => true,
|
||||
&CFGDEF_COMMAND =>
|
||||
{
|
||||
&CFGCMD_ARCHIVE_GET => {},
|
||||
&CFGCMD_ARCHIVE_PUSH => {},
|
||||
&CFGCMD_BACKUP => {},
|
||||
&CFGCMD_CHECK => {},
|
||||
&CFGCMD_EXPIRE => {},
|
||||
&CFGCMD_INFO => {},
|
||||
&CFGCMD_LOCAL => {},
|
||||
&CFGCMD_REMOTE => {},
|
||||
&CFGCMD_RESTORE => {},
|
||||
&CFGCMD_STANZA_CREATE => {},
|
||||
&CFGCMD_STANZA_UPGRADE => {},
|
||||
&CFGCMD_START => {},
|
||||
&CFGCMD_STOP => {},
|
||||
},
|
||||
},
|
||||
|
||||
&CFGOPT_PROTOCOL_TIMEOUT =>
|
||||
{
|
||||
&CFGDEF_SECTION => CFGDEF_SECTION_GLOBAL,
|
||||
|
@ -192,7 +192,7 @@ if (condition)
|
||||
<p>Marks code that is not tested for one reason or another. This should be kept to a minimum and an excuse given for each instance.</p>
|
||||
|
||||
<code-block>
|
||||
exit(EXIT_FAILURE); // {uncoverable - test harness does not support non-zero exit}
|
||||
exit(EXIT_FAILURE); // {uncovered - test harness does not support non-zero exit}
|
||||
</code-block>
|
||||
|
||||
<p>Subsequent code that is uncovered for the same reason is marked with `// {+uncovered}`.</p>
|
||||
|
@ -41,6 +41,10 @@
|
||||
<p>Implement <cmd>version</cmd> command in C.</p>
|
||||
</release-item>
|
||||
|
||||
<release-item>
|
||||
<p>Config parsing implemented in C.</p>
|
||||
</release-item>
|
||||
|
||||
<release-item>
|
||||
<p>Add <code>Buffer</code>, <code>Ini</code>, <code>KeyValue</code>, <code>List</code>, <code>Storage</code>, <code>String</code>, <code>StringList</code>, <code>Variant</code>, and <code>VariantList</code> objects.</p>
|
||||
</release-item>
|
||||
@ -65,10 +69,6 @@
|
||||
<p>Replace <code>cfgCommandTotal()</code>/<code>cfgOptionTotal()</code> functions with constants. The constants are applicable in more cases and allow the compiler to optimize certain loops more efficiently.</p>
|
||||
</release-item>
|
||||
|
||||
<release-item>
|
||||
<p>More config parsing in C in preparation for all config parsing in C.</p>
|
||||
</release-item>
|
||||
|
||||
<release-item>
|
||||
<p>Refactor code to make valgrind happy.</p>
|
||||
</release-item>
|
||||
|
@ -82,6 +82,13 @@ my @stryCFile =
|
||||
'common/error.c',
|
||||
'common/errorType.c',
|
||||
'common/memContext.c',
|
||||
'common/type/buffer.c',
|
||||
'common/type/keyValue.c',
|
||||
'common/type/list.c',
|
||||
'common/type/string.c',
|
||||
'common/type/stringList.c',
|
||||
'common/type/variant.c',
|
||||
'common/type/variantList.c',
|
||||
'config/config.c',
|
||||
'config/define.c',
|
||||
'postgres/pageChecksum.c',
|
||||
|
@ -159,6 +159,7 @@ sub libcAutoExportTag
|
||||
'CFGOPT_NEUTRAL_UMASK',
|
||||
'CFGOPT_ONLINE',
|
||||
'CFGOPT_OUTPUT',
|
||||
'CFGOPT_PERL_OPTION',
|
||||
'CFGOPT_PROCESS',
|
||||
'CFGOPT_PROCESS_MAX',
|
||||
'CFGOPT_PROTOCOL_TIMEOUT',
|
||||
|
@ -73,6 +73,7 @@ Option constants
|
||||
#define CFGOPT_NEUTRAL_UMASK cfgOptNeutralUmask
|
||||
#define CFGOPT_ONLINE cfgOptOnline
|
||||
#define CFGOPT_OUTPUT cfgOptOutput
|
||||
#define CFGOPT_PERL_OPTION cfgOptPerlOption
|
||||
#define CFGOPT_PROCESS cfgOptProcess
|
||||
#define CFGOPT_PROCESS_MAX cfgOptProcessMax
|
||||
#define CFGOPT_PROTOCOL_TIMEOUT cfgOptProtocolTimeout
|
||||
|
@ -32,10 +32,12 @@ ERROR_DEFINE(ERROR_CODE_MIN, AssertError, RuntimeError);
|
||||
|
||||
ERROR_DEFINE(ERROR_CODE_MIN + 04, FormatError, RuntimeError);
|
||||
ERROR_DEFINE(ERROR_CODE_MIN + 05, CommandRequiredError, FormatError);
|
||||
ERROR_DEFINE(ERROR_CODE_MIN + 06, OptionInvalidError, RuntimeError);
|
||||
ERROR_DEFINE(ERROR_CODE_MIN + 07, OptionInvalidValueError, RuntimeError);
|
||||
ERROR_DEFINE(ERROR_CODE_MIN + 12, OptionRequiredError, RuntimeError);
|
||||
ERROR_DEFINE(ERROR_CODE_MIN + 16, FileOpenError, RuntimeError);
|
||||
ERROR_DEFINE(ERROR_CODE_MIN + 17, FileReadError, RuntimeError);
|
||||
ERROR_DEFINE(ERROR_CODE_MIN + 23, CommandInvalidError, FormatError);
|
||||
ERROR_DEFINE(ERROR_CODE_MIN + 31, OptionInvalidError, FormatError);
|
||||
ERROR_DEFINE(ERROR_CODE_MIN + 39, FileWriteError, RuntimeError);
|
||||
ERROR_DEFINE(ERROR_CODE_MIN + 69, MemoryError, RuntimeError);
|
||||
ERROR_DEFINE(ERROR_CODE_MIN + 70, CipherError, FormatError);
|
||||
|
@ -18,10 +18,12 @@ ERROR_DECLARE(AssertError);
|
||||
|
||||
ERROR_DECLARE(FormatError);
|
||||
ERROR_DECLARE(CommandRequiredError);
|
||||
ERROR_DECLARE(CommandInvalidError);
|
||||
ERROR_DECLARE(OptionInvalidError);
|
||||
ERROR_DECLARE(OptionInvalidValueError);
|
||||
ERROR_DECLARE(OptionRequiredError);
|
||||
ERROR_DECLARE(FileOpenError);
|
||||
ERROR_DECLARE(FileReadError);
|
||||
ERROR_DECLARE(CommandInvalidError);
|
||||
ERROR_DECLARE(FileWriteError);
|
||||
ERROR_DECLARE(MemoryError);
|
||||
ERROR_DECLARE(CipherError);
|
||||
|
@ -898,6 +898,14 @@ ConfigOptionData configOptionData[CFG_OPTION_TOTAL] = CONFIG_OPTION_LIST
|
||||
CONFIG_OPTION_DEFINE_ID(cfgDefOptOutput)
|
||||
)
|
||||
|
||||
//------------------------------------------------------------------------------------------------------------------------------
|
||||
CONFIG_OPTION
|
||||
(
|
||||
CONFIG_OPTION_NAME("perl-option")
|
||||
CONFIG_OPTION_INDEX(0)
|
||||
CONFIG_OPTION_DEFINE_ID(cfgDefOptPerlOption)
|
||||
)
|
||||
|
||||
//------------------------------------------------------------------------------------------------------------------------------
|
||||
CONFIG_OPTION
|
||||
(
|
||||
|
@ -9,12 +9,12 @@ Automatically generated by Build.pm -- do not modify directly.
|
||||
/***********************************************************************************************************************************
|
||||
Command constants
|
||||
***********************************************************************************************************************************/
|
||||
#define CFG_COMMAND_TOTAL 15
|
||||
#define CFG_COMMAND_TOTAL 16
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Option constants
|
||||
***********************************************************************************************************************************/
|
||||
#define CFG_OPTION_TOTAL 138
|
||||
#define CFG_OPTION_TOTAL 139
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Command enum
|
||||
@ -89,6 +89,7 @@ typedef enum
|
||||
cfgOptNeutralUmask,
|
||||
cfgOptOnline,
|
||||
cfgOptOutput,
|
||||
cfgOptPerlOption,
|
||||
cfgOptProcess,
|
||||
cfgOptProcessMax,
|
||||
cfgOptProtocolTimeout,
|
||||
|
@ -4,6 +4,7 @@ Command and Option Configuration
|
||||
#include <string.h>
|
||||
|
||||
#include "common/error.h"
|
||||
#include "common/memContext.h"
|
||||
#include "config/config.h"
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
@ -52,6 +53,11 @@ Include the automatically generated configuration data
|
||||
***********************************************************************************************************************************/
|
||||
#include "config.auto.c"
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Store the config memory context
|
||||
***********************************************************************************************************************************/
|
||||
MemContext *configMemContext = NULL;
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Store the current command
|
||||
|
||||
@ -59,6 +65,67 @@ This is generally set by the command parser but can also be set by during execut
|
||||
***********************************************************************************************************************************/
|
||||
ConfigCommand command = cfgCmdNone;
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Store the location of the executable
|
||||
***********************************************************************************************************************************/
|
||||
String *exe = NULL;
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Was help requested for the command?
|
||||
***********************************************************************************************************************************/
|
||||
bool help = false;
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Store the list of parameters passed to the command
|
||||
***********************************************************************************************************************************/
|
||||
StringList *paramList = NULL;
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Map options names and indexes to option definitions.
|
||||
***********************************************************************************************************************************/
|
||||
typedef struct ConfigOptionValue
|
||||
{
|
||||
bool valid:1;
|
||||
bool negate:1;
|
||||
unsigned int source:2;
|
||||
|
||||
Variant *value;
|
||||
} ConfigOptionValue;
|
||||
|
||||
ConfigOptionValue configOptionValue[CFG_OPTION_TOTAL];
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Initialize or reinitialize the configuration data
|
||||
***********************************************************************************************************************************/
|
||||
void
|
||||
cfgInit()
|
||||
{
|
||||
// Reset configuration
|
||||
command = cfgCmdNone;
|
||||
exe = NULL;
|
||||
help = false;
|
||||
paramList = NULL;
|
||||
memset(&configOptionValue, 0, sizeof(configOptionValue));
|
||||
|
||||
// Free the old context
|
||||
if (configMemContext != NULL)
|
||||
{
|
||||
memContextFree(configMemContext);
|
||||
configMemContext = NULL;
|
||||
}
|
||||
|
||||
// Allocate configuration context as a child of the top context
|
||||
MEM_CONTEXT_BEGIN(memContextTop())
|
||||
{
|
||||
MEM_CONTEXT_NEW_BEGIN("configuration")
|
||||
{
|
||||
configMemContext = MEM_CONTEXT_NEW();
|
||||
}
|
||||
MEM_CONTEXT_NEW_END();
|
||||
}
|
||||
MEM_CONTEXT_END();
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Get the current command
|
||||
***********************************************************************************************************************************/
|
||||
@ -87,6 +154,21 @@ cfgCommandCheck(ConfigCommand commandId)
|
||||
THROW(AssertError, "command id %d invalid - must be >= 0 and < %d", commandId, CFG_COMMAND_TOTAL);
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Was help requested?
|
||||
***********************************************************************************************************************************/
|
||||
bool
|
||||
cfgCommandHelp()
|
||||
{
|
||||
return help;
|
||||
}
|
||||
|
||||
void
|
||||
cfgCommandHelpSet(bool helpParam)
|
||||
{
|
||||
help = helpParam;
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Get the define id for this command
|
||||
|
||||
@ -108,11 +190,11 @@ cfgCommandId(const char *commandName)
|
||||
{
|
||||
ConfigCommand commandId;
|
||||
|
||||
for (commandId = 0; commandId < CFG_COMMAND_TOTAL; commandId++)
|
||||
for (commandId = 0; commandId < cfgCmdNone; commandId++)
|
||||
if (strcmp(commandName, configCommandData[commandId].name) == 0)
|
||||
break;
|
||||
|
||||
if (commandId == CFG_COMMAND_TOTAL)
|
||||
if (commandId == cfgCmdNone)
|
||||
THROW(AssertError, "invalid command '%s'", commandName);
|
||||
|
||||
return commandId;
|
||||
@ -128,6 +210,54 @@ cfgCommandName(ConfigCommand commandId)
|
||||
return configCommandData[commandId].name;
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Command parameters, if any
|
||||
***********************************************************************************************************************************/
|
||||
const StringList *
|
||||
cfgCommandParam()
|
||||
{
|
||||
if (paramList == NULL)
|
||||
{
|
||||
MEM_CONTEXT_BEGIN(configMemContext)
|
||||
{
|
||||
paramList = strLstNew();
|
||||
}
|
||||
MEM_CONTEXT_END();
|
||||
}
|
||||
|
||||
return paramList;
|
||||
}
|
||||
|
||||
void
|
||||
cfgCommandParamSet(const StringList *param)
|
||||
{
|
||||
MEM_CONTEXT_BEGIN(configMemContext)
|
||||
{
|
||||
paramList = strLstDup(param);
|
||||
}
|
||||
MEM_CONTEXT_END();
|
||||
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Command parameters, if any
|
||||
***********************************************************************************************************************************/
|
||||
const String *
|
||||
cfgExe()
|
||||
{
|
||||
return exe;
|
||||
}
|
||||
|
||||
void
|
||||
cfgExeSet(const String *exeParam)
|
||||
{
|
||||
MEM_CONTEXT_BEGIN(configMemContext)
|
||||
{
|
||||
exe = strDup(exeParam);
|
||||
}
|
||||
MEM_CONTEXT_END();
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Ensure that option id is valid
|
||||
***********************************************************************************************************************************/
|
||||
@ -211,3 +341,224 @@ cfgOptionName(ConfigOption optionId)
|
||||
cfgOptionCheck(optionId);
|
||||
return configOptionData[optionId].name;
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Was the option negated?
|
||||
***********************************************************************************************************************************/
|
||||
bool
|
||||
cfgOptionNegate(ConfigOption optionId)
|
||||
{
|
||||
cfgOptionCheck(optionId);
|
||||
return configOptionValue[optionId].negate;
|
||||
}
|
||||
|
||||
void
|
||||
cfgOptionNegateSet(ConfigOption optionId, bool negate)
|
||||
{
|
||||
cfgOptionCheck(optionId);
|
||||
|
||||
configOptionValue[optionId].negate = negate;
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Get and set config options
|
||||
***********************************************************************************************************************************/
|
||||
const Variant *
|
||||
cfgOption(ConfigSource optionId)
|
||||
{
|
||||
cfgOptionCheck(optionId);
|
||||
return configOptionValue[optionId].value;
|
||||
}
|
||||
|
||||
bool
|
||||
cfgOptionBool(ConfigSource optionId)
|
||||
{
|
||||
cfgOptionCheck(optionId);
|
||||
|
||||
if (varType(configOptionValue[optionId].value) != varTypeBool)
|
||||
THROW(AssertError, "option '%s' is not type 'bool'", cfgOptionName(optionId));
|
||||
|
||||
return varBool(configOptionValue[optionId].value);
|
||||
}
|
||||
|
||||
double
|
||||
cfgOptionDbl(ConfigSource optionId)
|
||||
{
|
||||
cfgOptionCheck(optionId);
|
||||
|
||||
if (varType(configOptionValue[optionId].value) != varTypeDouble)
|
||||
THROW(AssertError, "option '%s' is not type 'double'", cfgOptionName(optionId));
|
||||
|
||||
return varDbl(configOptionValue[optionId].value);
|
||||
}
|
||||
|
||||
int
|
||||
cfgOptionInt(ConfigSource optionId)
|
||||
{
|
||||
cfgOptionCheck(optionId);
|
||||
|
||||
if (varType(configOptionValue[optionId].value) != varTypeInt)
|
||||
THROW(AssertError, "option '%s' is not type 'int'", cfgOptionName(optionId));
|
||||
|
||||
return varInt(configOptionValue[optionId].value);
|
||||
}
|
||||
|
||||
const KeyValue *
|
||||
cfgOptionKv(ConfigSource optionId)
|
||||
{
|
||||
cfgOptionCheck(optionId);
|
||||
|
||||
if (varType(configOptionValue[optionId].value) != varTypeKeyValue)
|
||||
THROW(AssertError, "option '%s' is not type 'KeyValue'", cfgOptionName(optionId));
|
||||
|
||||
return varKv(configOptionValue[optionId].value);
|
||||
}
|
||||
|
||||
const VariantList *
|
||||
cfgOptionLst(ConfigSource optionId)
|
||||
{
|
||||
cfgOptionCheck(optionId);
|
||||
|
||||
if (configOptionValue[optionId].value == NULL)
|
||||
{
|
||||
MEM_CONTEXT_BEGIN(configMemContext)
|
||||
{
|
||||
configOptionValue[optionId].value = varNewVarLst(varLstNew());
|
||||
}
|
||||
MEM_CONTEXT_END();
|
||||
}
|
||||
else if (varType(configOptionValue[optionId].value) != varTypeVariantList)
|
||||
THROW(AssertError, "option '%s' is not type 'VariantList'", cfgOptionName(optionId));
|
||||
|
||||
return varVarLst(configOptionValue[optionId].value);
|
||||
}
|
||||
|
||||
const String *
|
||||
cfgOptionStr(ConfigSource optionId)
|
||||
{
|
||||
cfgOptionCheck(optionId);
|
||||
|
||||
const String *result = NULL;
|
||||
|
||||
if (configOptionValue[optionId].value != NULL)
|
||||
{
|
||||
if (varType(configOptionValue[optionId].value) != varTypeString)
|
||||
THROW(AssertError, "option '%s' is not type 'String'", cfgOptionName(optionId));
|
||||
|
||||
result = varStr(configOptionValue[optionId].value);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void
|
||||
cfgOptionSet(ConfigOption optionId, ConfigSource source, const Variant *value)
|
||||
{
|
||||
cfgOptionCheck(optionId);
|
||||
|
||||
MEM_CONTEXT_BEGIN(configMemContext)
|
||||
{
|
||||
// Set the source
|
||||
configOptionValue[optionId].source = source;
|
||||
|
||||
// Store old value
|
||||
Variant *valueOld = configOptionValue[optionId].value;
|
||||
|
||||
// Only set value if it is not null
|
||||
if (value != NULL)
|
||||
{
|
||||
switch (cfgDefOptionType(cfgOptionDefIdFromId(optionId)))
|
||||
{
|
||||
case cfgDefOptTypeBoolean:
|
||||
{
|
||||
if (varType(value) == varTypeBool)
|
||||
configOptionValue[optionId].value = varDup(value);
|
||||
else
|
||||
configOptionValue[optionId].value = varNewBool(varBoolForce(value));
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case cfgDefOptTypeFloat:
|
||||
{
|
||||
if (varType(value) == varTypeDouble)
|
||||
configOptionValue[optionId].value = varDup(value);
|
||||
else
|
||||
configOptionValue[optionId].value = varNewDbl(varDblForce(value));
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case cfgDefOptTypeInteger:
|
||||
{
|
||||
if (varType(value) == varTypeInt)
|
||||
configOptionValue[optionId].value = varDup(value);
|
||||
else
|
||||
configOptionValue[optionId].value = varNewInt(varIntForce(value));
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case cfgDefOptTypeHash:
|
||||
{
|
||||
if (varType(value) == varTypeKeyValue)
|
||||
configOptionValue[optionId].value = varDup(value);
|
||||
else
|
||||
THROW(AssertError, "option '%s' must be set with KeyValue variant", cfgOptionName(optionId));
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case cfgDefOptTypeList:
|
||||
{
|
||||
if (varType(value) == varTypeVariantList)
|
||||
configOptionValue[optionId].value = varDup(value);
|
||||
else
|
||||
THROW(AssertError, "option '%s' must be set with VariantList variant", cfgOptionName(optionId));
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case cfgDefOptTypeString:
|
||||
if (varType(value) == varTypeString)
|
||||
configOptionValue[optionId].value = varDup(value);
|
||||
else
|
||||
THROW(AssertError, "option '%s' must be set with String variant", cfgOptionName(optionId));
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Free old value
|
||||
if (valueOld != NULL)
|
||||
varFree(valueOld);
|
||||
}
|
||||
MEM_CONTEXT_END();
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
How was the option set (default, param, config)?
|
||||
***********************************************************************************************************************************/
|
||||
ConfigSource
|
||||
cfgOptionSource(ConfigSource optionId)
|
||||
{
|
||||
cfgOptionCheck(optionId);
|
||||
return configOptionValue[optionId].source;
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Is the option valid for this command?
|
||||
***********************************************************************************************************************************/
|
||||
bool
|
||||
cfgOptionValid(ConfigOption optionId)
|
||||
{
|
||||
cfgOptionCheck(optionId);
|
||||
return configOptionValue[optionId].valid;
|
||||
}
|
||||
|
||||
void
|
||||
cfgOptionValidSet(ConfigOption optionId, bool valid)
|
||||
{
|
||||
cfgOptionCheck(optionId);
|
||||
configOptionValue[optionId].valid = valid;
|
||||
}
|
||||
|
@ -9,20 +9,54 @@ Command and Option Configuration
|
||||
|
||||
#include "config/config.auto.h"
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Option source enum - defines where an option value came from
|
||||
***********************************************************************************************************************************/
|
||||
typedef enum
|
||||
{
|
||||
cfgSourceDefault, // Default value
|
||||
cfgSourceParam, // Passed as command-line parameter
|
||||
cfgSourceConfig, // From configuration file
|
||||
} ConfigSource;
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Functions
|
||||
***********************************************************************************************************************************/
|
||||
void cfgInit();
|
||||
|
||||
ConfigCommand cfgCommand();
|
||||
bool cfgCommandHelp();
|
||||
int cfgCommandId(const char *commandName);
|
||||
const char *cfgCommandName(ConfigCommand commandId);
|
||||
ConfigDefineCommand cfgCommandDefIdFromId(ConfigCommand commandId);
|
||||
const StringList *cfgCommandParam();
|
||||
|
||||
void cfgCommandHelpSet(bool helpParam);
|
||||
void cfgCommandParamSet(const StringList *param);
|
||||
void cfgCommandSet(ConfigCommand commandParam);
|
||||
|
||||
const String *cfgExe();
|
||||
void cfgExeSet(const String *exeParam);
|
||||
|
||||
const Variant *cfgOption(ConfigSource optionId);
|
||||
bool cfgOptionBool(ConfigSource optionId);
|
||||
ConfigDefineOption cfgOptionDefIdFromId(ConfigOption optionId);
|
||||
double cfgOptionDbl(ConfigSource optionId);
|
||||
int cfgOptionId(const char *optionName);
|
||||
ConfigOption cfgOptionIdFromDefId(ConfigDefineOption optionDefId, int index);
|
||||
int cfgOptionIndex(ConfigOption optionId);
|
||||
int cfgOptionIndexTotal(ConfigOption optionDefId);
|
||||
int cfgOptionInt(ConfigSource optionId);
|
||||
const KeyValue *cfgOptionKv(ConfigSource optionId);
|
||||
const VariantList *cfgOptionLst(ConfigSource optionId);
|
||||
const char *cfgOptionName(ConfigOption optionId);
|
||||
ConfigDefineOption cfgOptionDefIdFromId(ConfigOption optionId);
|
||||
bool cfgOptionNegate(ConfigOption optionId);
|
||||
ConfigSource cfgOptionSource(ConfigSource optionId);
|
||||
const String *cfgOptionStr(ConfigSource optionId);
|
||||
bool cfgOptionValid(ConfigOption optionId);
|
||||
|
||||
void cfgOptionNegateSet(ConfigOption optionId, bool negate);
|
||||
void cfgOptionSet(ConfigOption optionId, ConfigSource source, const Variant *value);
|
||||
void cfgOptionValidSet(ConfigOption optionId, bool valid);
|
||||
|
||||
#endif
|
||||
|
@ -1495,6 +1495,36 @@ ConfigDefineOptionData configDefineOptionData[] = CFGDEFDATA_OPTION_LIST
|
||||
)
|
||||
)
|
||||
|
||||
// -----------------------------------------------------------------------------------------------------------------------------
|
||||
CFGDEFDATA_OPTION
|
||||
(
|
||||
CFGDEFDATA_OPTION_NAME("perl-option")
|
||||
CFGDEFDATA_OPTION_REQUIRED(false)
|
||||
CFGDEFDATA_OPTION_SECTION(cfgDefSectionGlobal)
|
||||
CFGDEFDATA_OPTION_TYPE(cfgDefOptTypeList)
|
||||
|
||||
CFGDEFDATA_OPTION_INDEX_TOTAL(1)
|
||||
CFGDEFDATA_OPTION_NEGATE(true)
|
||||
CFGDEFDATA_OPTION_SECURE(false)
|
||||
|
||||
CFGDEFDATA_OPTION_COMMAND_LIST
|
||||
(
|
||||
CFGDEFDATA_OPTION_COMMAND(cfgDefCmdArchiveGet)
|
||||
CFGDEFDATA_OPTION_COMMAND(cfgDefCmdArchivePush)
|
||||
CFGDEFDATA_OPTION_COMMAND(cfgDefCmdBackup)
|
||||
CFGDEFDATA_OPTION_COMMAND(cfgDefCmdCheck)
|
||||
CFGDEFDATA_OPTION_COMMAND(cfgDefCmdExpire)
|
||||
CFGDEFDATA_OPTION_COMMAND(cfgDefCmdInfo)
|
||||
CFGDEFDATA_OPTION_COMMAND(cfgDefCmdLocal)
|
||||
CFGDEFDATA_OPTION_COMMAND(cfgDefCmdRemote)
|
||||
CFGDEFDATA_OPTION_COMMAND(cfgDefCmdRestore)
|
||||
CFGDEFDATA_OPTION_COMMAND(cfgDefCmdStanzaCreate)
|
||||
CFGDEFDATA_OPTION_COMMAND(cfgDefCmdStanzaUpgrade)
|
||||
CFGDEFDATA_OPTION_COMMAND(cfgDefCmdStart)
|
||||
CFGDEFDATA_OPTION_COMMAND(cfgDefCmdStop)
|
||||
)
|
||||
)
|
||||
|
||||
// -----------------------------------------------------------------------------------------------------------------------------
|
||||
CFGDEFDATA_OPTION
|
||||
(
|
||||
|
@ -91,6 +91,7 @@ typedef enum
|
||||
cfgDefOptNeutralUmask,
|
||||
cfgDefOptOnline,
|
||||
cfgDefOptOutput,
|
||||
cfgDefOptPerlOption,
|
||||
cfgDefOptProcess,
|
||||
cfgDefOptProcessMax,
|
||||
cfgDefOptProtocolTimeout,
|
||||
|
@ -17,6 +17,11 @@ typedef enum
|
||||
cfgDefSectionStanza, // command-line of in any config stanza section
|
||||
} ConfigDefSection;
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Define global section name
|
||||
***********************************************************************************************************************************/
|
||||
#define CFGDEF_SECTION_GLOBAL "global"
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Functions
|
||||
***********************************************************************************************************************************/
|
||||
|
@ -957,6 +957,15 @@ static const struct option optionList[] =
|
||||
.has_arg = required_argument,
|
||||
.val = PARSE_OPTION_FLAG | cfgOptOutput,
|
||||
},
|
||||
{
|
||||
.name = "perl-option",
|
||||
.has_arg = required_argument,
|
||||
.val = PARSE_OPTION_FLAG | cfgOptPerlOption,
|
||||
},
|
||||
{
|
||||
.name = "no-perl-option",
|
||||
.val = PARSE_OPTION_FLAG | PARSE_NEGATE_FLAG | cfgOptPerlOption,
|
||||
},
|
||||
{
|
||||
.name = "process",
|
||||
.has_arg = required_argument,
|
||||
@ -1240,12 +1249,6 @@ static const struct option optionList[] =
|
||||
.has_arg = required_argument,
|
||||
.val = PARSE_OPTION_FLAG | cfgOptType,
|
||||
},
|
||||
// Perl option is ignored by normal config parsing
|
||||
{
|
||||
.name = "perl-option",
|
||||
.has_arg = required_argument,
|
||||
.val = 0,
|
||||
},
|
||||
// Terminate option list
|
||||
{
|
||||
.name = NULL
|
||||
|
@ -4,13 +4,16 @@ Command and Option Parse
|
||||
#include <assert.h>
|
||||
#include <getopt.h>
|
||||
#include <string.h>
|
||||
#include <strings.h>
|
||||
|
||||
#include "common/error.h"
|
||||
#include "common/ini.h"
|
||||
#include "common/memContext.h"
|
||||
#include "config/parse.h"
|
||||
#include "storage/storage.h"
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Include the automatically generated configuration data
|
||||
Parse option flags
|
||||
***********************************************************************************************************************************/
|
||||
// Offset the option values so they don't conflict with getopt_long return codes
|
||||
#define PARSE_OPTION_FLAG (1 << 31)
|
||||
@ -21,19 +24,44 @@ Include the automatically generated configuration data
|
||||
// Mask to exclude all flags and get at the actual option id (only 12 bits allowed for option id, the rest reserved for flags)
|
||||
#define PARSE_OPTION_MASK 0xFFF
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Include automatically generated data structure for getopt_long()
|
||||
***********************************************************************************************************************************/
|
||||
#include "parse.auto.c"
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Parse the command-line arguments and produce preliminary parse data
|
||||
|
||||
This function only checks for obvious errors. Dependencies, types, etc, are checked later when data from the config file is
|
||||
available.
|
||||
Struct to hold options parsed from the command line
|
||||
***********************************************************************************************************************************/
|
||||
ParseData *
|
||||
configParseArg(int argListSize, const char *argList[])
|
||||
typedef struct ParseOption
|
||||
{
|
||||
// Allocate memory for the parse data
|
||||
ParseData *parseData = memNew(sizeof(ParseData));
|
||||
bool found:1; // Was the option found on the command line?
|
||||
bool negate:1; // Was the option negated on the command line?
|
||||
unsigned int source:2; // Where was to option found?
|
||||
StringList *valueList; // List of values found
|
||||
} ParseOption;
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Parse the command-line arguments and config file to produce final config data
|
||||
***********************************************************************************************************************************/
|
||||
void
|
||||
configParse(int argListSize, const char *argList[])
|
||||
{
|
||||
// Initialize configuration
|
||||
cfgInit();
|
||||
|
||||
MEM_CONTEXT_TEMP_BEGIN()
|
||||
{
|
||||
// Set the exe
|
||||
cfgExeSet(strNew(argList[0]));
|
||||
|
||||
// Phase 1: parse command line parameters
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
int option; // Code returned by getopt_long
|
||||
int optionListIdx; // Index of option is list (if an option was returned)
|
||||
ConfigOption optionId; // Option id extracted from option var
|
||||
bool negate; // Option is being negated
|
||||
bool argFound = false; // Track args found to decide on error or help at the end
|
||||
StringList *commandParamList = NULL; // List of command parameters
|
||||
|
||||
// Reset optind to 1 in case getopt_long has been called before
|
||||
optind = 1;
|
||||
@ -41,57 +69,52 @@ configParseArg(int argListSize, const char *argList[])
|
||||
// Don't error automatically on unknown options - they will be processed in the loop below
|
||||
opterr = false;
|
||||
|
||||
// List of parsed options
|
||||
ParseOption parseOptionList[CFG_OPTION_TOTAL];
|
||||
memset(&parseOptionList, 0, sizeof(parseOptionList));
|
||||
|
||||
// Only the first non-option parameter should be treated as a command so track if the command has been set
|
||||
bool commandSet = false;
|
||||
|
||||
// Parse options
|
||||
int option; // Code returned by getopt_long
|
||||
int optionListIdx; // Index of option is list (if an option was returned)
|
||||
ConfigOption optionId; // Option id extracted from option var
|
||||
bool negate; // Option is being negated
|
||||
bool argFound = false; // Track args found to decide on error or help at the end
|
||||
|
||||
while ((option = getopt_long(argListSize, (char **)argList, "-:", optionList, &optionListIdx)) != -1)
|
||||
{
|
||||
switch (option)
|
||||
{
|
||||
// Add perl options (if any) to the list
|
||||
case 0:
|
||||
if (parseData->perlOptionList == NULL)
|
||||
parseData->perlOptionList = strLstNew();
|
||||
|
||||
strLstAdd(parseData->perlOptionList, strNew(optarg));
|
||||
break;
|
||||
|
||||
// Parse arguments that are not options, i.e. commands and parameters passed to commands
|
||||
case 1:
|
||||
{
|
||||
// The first argument should be the command
|
||||
if (!commandSet)
|
||||
{
|
||||
// Try getting the command from the valid command list
|
||||
TRY_BEGIN()
|
||||
{
|
||||
parseData->command = cfgCommandId(argList[optind - 1]);
|
||||
cfgCommandSet(cfgCommandId(argList[optind - 1]));
|
||||
}
|
||||
// Assert error means the command does not exist, which is correct for all usages but this one (since we don't
|
||||
// have any control over what the user passes), so modify the error code and message.
|
||||
// Assert error means the command does not exist, which is correct for all usages but this one (since we
|
||||
// don't have any control over what the user passes), so modify the error code and message.
|
||||
CATCH(AssertError)
|
||||
{
|
||||
THROW(CommandInvalidError, "invalid command '%s'", argList[optind - 1]);
|
||||
}
|
||||
TRY_END();
|
||||
|
||||
if (cfgCommand() == cfgCmdHelp)
|
||||
cfgCommandHelpSet(true);
|
||||
else
|
||||
commandSet = true;
|
||||
}
|
||||
// Additioal arguments are command arguments
|
||||
// Additional arguments are command arguments
|
||||
else
|
||||
{
|
||||
if (parseData->commandArgList == NULL)
|
||||
parseData->commandArgList = strLstNew();
|
||||
if (commandParamList == NULL)
|
||||
commandParamList = strLstNew();
|
||||
|
||||
strLstAdd(parseData->commandArgList, strNew(argList[optind - 1]));
|
||||
strLstAdd(commandParamList, strNew(argList[optind - 1]));
|
||||
}
|
||||
|
||||
commandSet = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// If the option is unknown then error
|
||||
case '?':
|
||||
@ -113,27 +136,28 @@ configParseArg(int argListSize, const char *argList[])
|
||||
assert(optionId < CFG_OPTION_TOTAL);
|
||||
|
||||
// If the the option has not been found yet then set it
|
||||
if (!parseData->parseOptionList[optionId].found)
|
||||
if (!parseOptionList[optionId].found)
|
||||
{
|
||||
parseData->parseOptionList[optionId].found = true;
|
||||
parseData->parseOptionList[optionId].negate = negate;
|
||||
parseOptionList[optionId].found = true;
|
||||
parseOptionList[optionId].negate = negate;
|
||||
parseOptionList[optionId].source = cfgSourceParam;
|
||||
|
||||
// Only set the argument if the option requires one
|
||||
if (optionList[optionListIdx].has_arg == required_argument)
|
||||
parseData->parseOptionList[optionId].valueList = strLstAdd(strLstNew(), strNew(optarg));
|
||||
parseOptionList[optionId].valueList = strLstAdd(strLstNew(), strNew(optarg));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Make sure option is not negated more than once. It probably wouldn't hurt anything to accept this case but
|
||||
// there's no point in allowing the user to be sloppy.
|
||||
if (parseData->parseOptionList[optionId].negate && negate)
|
||||
// Make sure option is not negated more than once. It probably wouldn't hurt anything to accept this case
|
||||
// but there's no point in allowing the user to be sloppy.
|
||||
if (parseOptionList[optionId].negate && negate)
|
||||
THROW(OptionInvalidError, "option '%s' is negated multiple times", cfgOptionName(optionId));
|
||||
|
||||
// Don't allow an option to be both set and negated
|
||||
if (parseData->parseOptionList[optionId].negate != negate)
|
||||
if (parseOptionList[optionId].negate != negate)
|
||||
THROW(OptionInvalidError, "option '%s' cannot be set and negated", cfgOptionName(optionId));
|
||||
|
||||
// Error if this option does not allow multiple arguments (!!! IMPLEMENT THIS IN DEFINE.C)
|
||||
// Error if this option does not allow multiple arguments
|
||||
if (!(cfgDefOptionType(cfgOptionDefIdFromId(optionId)) == cfgDefOptTypeHash ||
|
||||
cfgDefOptionType(cfgOptionDefIdFromId(optionId)) == cfgDefOptTypeList))
|
||||
{
|
||||
@ -141,7 +165,7 @@ configParseArg(int argListSize, const char *argList[])
|
||||
}
|
||||
|
||||
// Add the argument
|
||||
strLstAdd(parseData->parseOptionList[optionId].valueList, strNew(optarg));
|
||||
strLstAdd(parseOptionList[optionId].valueList, strNew(optarg));
|
||||
}
|
||||
|
||||
break;
|
||||
@ -152,32 +176,430 @@ configParseArg(int argListSize, const char *argList[])
|
||||
}
|
||||
|
||||
// Handle command not found
|
||||
if (!commandSet)
|
||||
if (!commandSet && !cfgCommandHelp())
|
||||
{
|
||||
// If there are args then error
|
||||
if (argFound)
|
||||
THROW(CommandRequiredError, "no command found");
|
||||
|
||||
// Otherwise set the comand to help
|
||||
parseData->command = cfgCmdHelp;
|
||||
cfgCommandHelpSet(true);
|
||||
}
|
||||
|
||||
// Return the parse data
|
||||
return parseData;
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Parse the command-line arguments and config file to produce final config data
|
||||
***********************************************************************************************************************************/
|
||||
void
|
||||
configParse(int argListSize, const char *argList[])
|
||||
{
|
||||
// Parse the command line
|
||||
ParseData *parseData = configParseArg(argListSize, argList);
|
||||
|
||||
// Set the command
|
||||
cfgCommandSet(parseData->command);
|
||||
|
||||
// Free the parse data
|
||||
memFree(parseData);
|
||||
// Set command params
|
||||
if (commandParamList != NULL)
|
||||
cfgCommandParamSet(commandParamList);
|
||||
|
||||
// Parse options from config file unless --no-config passed
|
||||
if (cfgCommand() != cfgCmdNone &&
|
||||
cfgCommand() != cfgCmdVersion &&
|
||||
cfgCommand() != cfgCmdHelp)
|
||||
{
|
||||
// Get the command definition id
|
||||
ConfigDefineCommand commandDefId = cfgCommandDefIdFromId(cfgCommand());
|
||||
|
||||
// Phase 2: parse config file
|
||||
// ---------------------------------------------------------------------------------------------------------------------
|
||||
if (!parseOptionList[cfgOptConfig].negate)
|
||||
{
|
||||
// Get the config file name from the command-line if it exists else default
|
||||
const String *configFile = NULL;
|
||||
|
||||
if (parseOptionList[cfgOptConfig].found)
|
||||
configFile = strLstGet(parseOptionList[cfgOptConfig].valueList, 0);
|
||||
else
|
||||
configFile = strNew(cfgDefOptionDefault(commandDefId, cfgOptionDefIdFromId(cfgOptConfig)));
|
||||
|
||||
// Load the ini file
|
||||
Buffer *buffer = storageGet(storageLocal(), configFile, !parseOptionList[cfgOptConfig].found);
|
||||
|
||||
// Load the config file if it was found
|
||||
if (buffer != NULL)
|
||||
{
|
||||
// Parse the ini file
|
||||
Ini *config = iniNew();
|
||||
iniParse(config, strNewBuf(buffer));
|
||||
|
||||
// Get the stanza name
|
||||
String *stanza = NULL;
|
||||
|
||||
if (parseOptionList[cfgOptStanza].found)
|
||||
stanza = strLstGet(parseOptionList[cfgOptStanza].valueList, 0);
|
||||
|
||||
// Build list of sections to search for options
|
||||
StringList *sectionList = strLstNew();
|
||||
|
||||
if (stanza != NULL)
|
||||
{
|
||||
strLstAdd(sectionList, strNewFmt("%s:%s", strPtr(stanza), cfgCommandName(cfgCommand())));
|
||||
strLstAdd(sectionList, stanza);
|
||||
}
|
||||
|
||||
strLstAdd(sectionList, strNewFmt(CFGDEF_SECTION_GLOBAL ":%s", cfgCommandName(cfgCommand())));
|
||||
strLstAdd(sectionList, strNew(CFGDEF_SECTION_GLOBAL));
|
||||
|
||||
// Loop through sections to search for options
|
||||
for (unsigned int sectionIdx = 0; sectionIdx < strLstSize(sectionList); sectionIdx++)
|
||||
{
|
||||
String *section = strLstGet(sectionList, sectionIdx);
|
||||
StringList *keyList = iniSectionKeyList(config, section);
|
||||
|
||||
// Loop through keys to search for options
|
||||
for (unsigned int keyIdx = 0; keyIdx < strLstSize(keyList); keyIdx++)
|
||||
{
|
||||
String *key = strLstGet(keyList, keyIdx);
|
||||
|
||||
// Find the optionName in the main list
|
||||
unsigned int optionIdx = 0;
|
||||
|
||||
while (optionList[optionIdx].name != NULL)
|
||||
{
|
||||
if (strcmp(strPtr(key), optionList[optionIdx].name) == 0)
|
||||
break;
|
||||
|
||||
optionIdx++;
|
||||
}
|
||||
|
||||
// Warn if the option not found
|
||||
if (optionList[optionIdx].name == NULL)
|
||||
{
|
||||
/// ??? Put warning here once there is a logging system
|
||||
continue;
|
||||
}
|
||||
// Warn if negate option found in config
|
||||
else if (optionList[optionIdx].val & PARSE_NEGATE_FLAG)
|
||||
{
|
||||
/// ??? Put warning here once there is a logging system
|
||||
continue;
|
||||
}
|
||||
|
||||
optionId = optionList[optionIdx].val & PARSE_OPTION_MASK;
|
||||
ConfigDefineOption optionDefId = cfgOptionDefIdFromId(optionId);
|
||||
|
||||
/// Warn if this option should be command-line only
|
||||
if (cfgDefOptionSection(optionDefId) == cfgDefSectionCommandLine)
|
||||
{
|
||||
/// ??? Put warning here once there is a logging system
|
||||
continue;
|
||||
}
|
||||
|
||||
// Continue if this option has already been found
|
||||
if (parseOptionList[optionId].found)
|
||||
continue;
|
||||
|
||||
// Continue if the option is not valid for this command
|
||||
if (!cfgDefOptionValid(commandDefId, optionDefId))
|
||||
{
|
||||
// Warn if it is in a command section
|
||||
if (sectionIdx % 2 == 0)
|
||||
{
|
||||
// ??? Put warning here once there is a logging system (and remove continue and braces)
|
||||
continue;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// Only get the option if it is valid for this section
|
||||
if ((stanza != NULL && sectionIdx < 2) || cfgDefOptionSection(optionDefId) == cfgDefSectionGlobal)
|
||||
{
|
||||
const Variant *value = iniGetDefault(config, section, key, NULL);
|
||||
|
||||
if (varType(value) == varTypeString && strSize(varStr(value)) == 0)
|
||||
THROW(OptionInvalidValueError, "section '%s', key '%s' must have a value", strPtr(section),
|
||||
strPtr(key));
|
||||
|
||||
parseOptionList[optionId].found = true;
|
||||
parseOptionList[optionId].source = cfgSourceConfig;
|
||||
|
||||
// Convert boolean to string
|
||||
if (cfgDefOptionType(optionDefId) == cfgDefOptTypeBoolean)
|
||||
{
|
||||
if (strcasecmp(strPtr(varStr(value)), "n") == 0)
|
||||
parseOptionList[optionId].negate = true;
|
||||
else if (strcasecmp(strPtr(varStr(value)), "y") != 0)
|
||||
THROW(OptionInvalidError, "boolean option '%s' must be 'y' or 'n'", strPtr(key));
|
||||
}
|
||||
// Else add the string value
|
||||
else if (varType(value) == varTypeString)
|
||||
{
|
||||
parseOptionList[optionId].valueList = strLstNew();
|
||||
strLstAdd(parseOptionList[optionId].valueList, varStr(value));
|
||||
}
|
||||
// Else add the string list
|
||||
else
|
||||
parseOptionList[optionId].valueList = strLstNewVarLst(varVarLst(value));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Phase 3: validate option definitions and load into configuration
|
||||
// ---------------------------------------------------------------------------------------------------------------------
|
||||
bool allResolved;
|
||||
bool optionResolved[CFG_OPTION_TOTAL] = {false};
|
||||
|
||||
do
|
||||
{
|
||||
// Assume that all dependencies will be resolved in this loop. This probably won't be true the first few times, but
|
||||
// eventually it will be or the do loop would never exit.
|
||||
allResolved = true;
|
||||
|
||||
// Loop through all options
|
||||
for (ConfigOption optionId = 0; optionId < CFG_OPTION_TOTAL; optionId++)
|
||||
{
|
||||
// Get the option data parsed from the command-line
|
||||
ParseOption *parseOption = &parseOptionList[optionId];
|
||||
|
||||
// Get the option definition id -- will be used to look up option rules
|
||||
ConfigDefineOption optionDefId = cfgOptionDefIdFromId(optionId);
|
||||
ConfigDefineOptionType optionDefType = cfgDefOptionType(optionDefId);
|
||||
|
||||
// Skip this option if it has already been resolved
|
||||
if (optionResolved[optionId])
|
||||
continue;
|
||||
|
||||
// Error if the option is not valid for this command
|
||||
if (parseOption->found && !cfgDefOptionValid(commandDefId, optionDefId))
|
||||
{
|
||||
THROW(
|
||||
OptionInvalidError, "option '%s' not valid for command '%s'", cfgOptionName(optionId),
|
||||
cfgCommandName(cfgCommand()));
|
||||
}
|
||||
|
||||
// Is the option valid for this command? If not, mark it as resolved since there is nothing more to do.
|
||||
cfgOptionValidSet(optionId, cfgDefOptionValid(commandDefId, optionDefId));
|
||||
|
||||
if (!cfgOptionValid(optionId))
|
||||
{
|
||||
optionResolved[optionId] = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Is the value set for this option?
|
||||
bool optionSet = parseOption->found && (optionDefType == cfgDefOptTypeBoolean || !parseOption->negate);
|
||||
|
||||
// Set negate flag
|
||||
cfgOptionNegateSet(optionId, parseOption->negate);
|
||||
|
||||
// Check option dependencies
|
||||
bool dependResolved = true;
|
||||
|
||||
if (cfgDefOptionDepend(commandDefId, optionDefId))
|
||||
{
|
||||
ConfigOption dependOptionId =
|
||||
cfgOptionIdFromDefId(cfgDefOptionDependOption(commandDefId, optionDefId), cfgOptionIndex(optionId));
|
||||
ConfigDefineOption dependOptionDefId = cfgOptionDefIdFromId(dependOptionId);
|
||||
ConfigDefineOptionType dependOptionDefType = cfgDefOptionType(dependOptionDefId);
|
||||
|
||||
// Make sure the depend option has been resolved, otherwise skip this option for now
|
||||
if (!optionResolved[dependOptionId])
|
||||
{
|
||||
allResolved = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get the depend option value
|
||||
const Variant *dependValue = cfgOption(dependOptionId);
|
||||
|
||||
if (dependValue != NULL)
|
||||
{
|
||||
if (dependOptionDefType == cfgDefOptTypeBoolean)
|
||||
{
|
||||
if (cfgOptionBool(dependOptionId))
|
||||
dependValue = varNewStrZ("1");
|
||||
else
|
||||
dependValue = varNewStrZ("0");
|
||||
}
|
||||
}
|
||||
|
||||
// Can't resolve if the depend option value is null
|
||||
if (dependValue == NULL)
|
||||
{
|
||||
dependResolved = false;
|
||||
|
||||
// if (optionSet)
|
||||
if (optionSet && parseOption->source == cfgSourceParam)
|
||||
{
|
||||
THROW(
|
||||
OptionInvalidError, "option '%s' not valid without option '%s'", cfgOptionName(optionId),
|
||||
cfgOptionName(dependOptionId));
|
||||
}
|
||||
}
|
||||
// If a depend list exists, make sure the value is in the list
|
||||
else if (cfgDefOptionDependValueTotal(commandDefId, optionDefId) > 0)
|
||||
{
|
||||
dependResolved = cfgDefOptionDependValueValid(commandDefId, optionDefId, strPtr(varStr(dependValue)));
|
||||
|
||||
// If not depend not resolved and option value is set then error
|
||||
if (!dependResolved && optionSet && parseOption->source == cfgSourceParam)
|
||||
{
|
||||
// Get the depend option name
|
||||
String *dependOptionName = strNew(cfgOptionName(dependOptionId));
|
||||
|
||||
// Build the list of possible depend values
|
||||
StringList *dependValueList = strLstNew();
|
||||
|
||||
for (int listIdx = 0; listIdx < cfgDefOptionDependValueTotal(commandDefId, optionDefId); listIdx++)
|
||||
{
|
||||
const char *dependValue = cfgDefOptionDependValue(commandDefId, optionDefId, listIdx);
|
||||
|
||||
// Build list based on depend option type
|
||||
switch (dependOptionDefType)
|
||||
{
|
||||
// Boolean outputs depend option name as no-* when false
|
||||
case cfgDefOptTypeBoolean:
|
||||
{
|
||||
if (strcmp(dependValue, "0") == 0)
|
||||
dependOptionName = strNewFmt("no-%s", cfgOptionName(dependOptionId));
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// String is output with quotes
|
||||
case cfgDefOptTypeString:
|
||||
{
|
||||
strLstAdd(dependValueList, strNewFmt("'%s'", dependValue));
|
||||
break;
|
||||
}
|
||||
|
||||
// Other types are output plain
|
||||
default:
|
||||
{
|
||||
strLstAddZ(dependValueList, dependValue); // {uncovered - no depends of other types}
|
||||
break; // {+uncovered}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Build the error string
|
||||
String *error = strNew("option '%s' not valid without option '%s'");
|
||||
|
||||
if (strLstSize(dependValueList) == 1)
|
||||
strCat(error, " = %s");
|
||||
else if (strLstSize(dependValueList) > 1)
|
||||
strCat(error, " in (%s)");
|
||||
|
||||
// Throw the error
|
||||
THROW(
|
||||
OptionInvalidError, strPtr(error), cfgOptionName(optionId), strPtr(dependOptionName),
|
||||
strPtr(strLstJoin(dependValueList, ", ")));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Is the option defined?
|
||||
if (optionSet && dependResolved)
|
||||
{
|
||||
if (optionDefType == cfgDefOptTypeBoolean)
|
||||
{
|
||||
cfgOptionSet(optionId, parseOption->source, varNewBool(!parseOption->negate));
|
||||
}
|
||||
else if (optionDefType == cfgDefOptTypeHash)
|
||||
{
|
||||
Variant *value = varNewKv();
|
||||
KeyValue *keyValue = varKv(value);
|
||||
|
||||
for (unsigned int listIdx = 0; listIdx < strLstSize(parseOption->valueList); listIdx++)
|
||||
{
|
||||
const char *pair = strPtr(strLstGet(parseOption->valueList, listIdx));
|
||||
const char *equal = strchr(pair, '=');
|
||||
|
||||
if (equal == NULL)
|
||||
{
|
||||
THROW(
|
||||
OptionInvalidError, "key/value '%s' not valid for '%s' option",
|
||||
strPtr(strLstGet(parseOption->valueList, listIdx)), cfgOptionName(optionId));
|
||||
}
|
||||
|
||||
kvPut(keyValue, varNewStr(strNewSzN(pair, equal - pair)), varNewStr(strNew(equal + 1)));
|
||||
}
|
||||
|
||||
cfgOptionSet(optionId, parseOption->source, value);
|
||||
}
|
||||
else if (optionDefType == cfgDefOptTypeList)
|
||||
{
|
||||
cfgOptionSet(optionId, parseOption->source, varNewVarLst(varLstNewStrLst(parseOption->valueList)));
|
||||
}
|
||||
else
|
||||
{
|
||||
String *value = strLstGet(parseOption->valueList, 0);
|
||||
|
||||
// If the option has an allow list then check it
|
||||
if (cfgDefOptionAllowList(commandDefId, optionDefId) &&
|
||||
!cfgDefOptionAllowListValueValid(commandDefId, optionDefId, strPtr(value)))
|
||||
{
|
||||
THROW(
|
||||
OptionInvalidValueError, "'%s' is not valid for '%s' option", strPtr(value),
|
||||
cfgOptionName(optionId));
|
||||
}
|
||||
|
||||
// If a numeric type check that the value is valid
|
||||
if (optionDefType == cfgDefOptTypeInteger || optionDefType == cfgDefOptTypeFloat)
|
||||
{
|
||||
double valueDbl = 0;
|
||||
|
||||
// Check that the value can be converted
|
||||
TRY_BEGIN()
|
||||
{
|
||||
if (optionDefType == cfgDefOptTypeInteger)
|
||||
valueDbl = varIntForce(varNewStr(value));
|
||||
else
|
||||
valueDbl = varDblForce(varNewStr(value));
|
||||
}
|
||||
CATCH_ANY()
|
||||
{
|
||||
THROW(
|
||||
OptionInvalidValueError, "'%s' is not valid for '%s' option", strPtr(value),
|
||||
cfgOptionName(optionId));
|
||||
}
|
||||
TRY_END();
|
||||
|
||||
// Check value range
|
||||
if (cfgDefOptionAllowRange(commandDefId, optionDefId) &&
|
||||
(valueDbl < cfgDefOptionAllowRangeMin(commandDefId, optionDefId) ||
|
||||
valueDbl > cfgDefOptionAllowRangeMax(commandDefId, optionDefId)))
|
||||
{
|
||||
THROW(
|
||||
OptionInvalidValueError, "'%s' is not valid for '%s' option", strPtr(value),
|
||||
cfgOptionName(optionId));
|
||||
}
|
||||
}
|
||||
|
||||
cfgOptionSet(optionId, parseOption->source, varNewStr(value));
|
||||
}
|
||||
}
|
||||
else if (dependResolved && parseOption->negate)
|
||||
cfgOptionSet(optionId, parseOption->source, NULL);
|
||||
// Else try to set a default
|
||||
else if (dependResolved)
|
||||
{
|
||||
// Get the default value for this option
|
||||
const char *value = cfgDefOptionDefault(commandDefId, optionDefId);
|
||||
|
||||
if (value != NULL)
|
||||
cfgOptionSet(optionId, cfgSourceDefault, varNewStrZ(value));
|
||||
else if (cfgOptionIndex(optionId) == 0 && cfgDefOptionRequired(commandDefId, optionDefId) &&
|
||||
!cfgCommandHelp())
|
||||
{
|
||||
const char *hint = "";
|
||||
|
||||
if (cfgDefOptionSection(optionDefId) == cfgDefSectionStanza)
|
||||
hint = "\nHINT: does this stanza exist?";
|
||||
|
||||
THROW(
|
||||
OptionRequiredError, "%s command requires option: %s%s", cfgCommandName(cfgCommand()),
|
||||
cfgOptionName(optionId), hint);
|
||||
}
|
||||
}
|
||||
|
||||
// Option is now resolved
|
||||
optionResolved[optionId] = true;
|
||||
}
|
||||
}
|
||||
while (!allResolved);
|
||||
}
|
||||
}
|
||||
MEM_CONTEXT_TEMP_END();
|
||||
}
|
||||
|
@ -6,29 +6,6 @@ Parse Configuration
|
||||
|
||||
#include "config/config.h"
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Struct to hold options parsed from the command line (??? move back to parse.c once perl exec works on full config data)
|
||||
***********************************************************************************************************************************/
|
||||
typedef struct ParseOption
|
||||
{
|
||||
bool found:1; // Was the option found on the command line?
|
||||
bool negate:1; // Was the option negated on the command line?
|
||||
StringList *valueList; // List of values found
|
||||
} ParseOption;
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Struct to hold all parsed data (??? move back to parse.c once perl exec works on full config data)
|
||||
***********************************************************************************************************************************/
|
||||
typedef struct ParseData
|
||||
{
|
||||
ConfigCommand command; // Command found
|
||||
StringList *perlOptionList; // List of perl options
|
||||
StringList *commandArgList; // List of command arguments
|
||||
ParseOption parseOptionList[CFG_OPTION_TOTAL]; // List of parsed options
|
||||
} ParseData;
|
||||
|
||||
ParseData *configParseArg(int argListSize, const char *argList[]);
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Functions
|
||||
***********************************************************************************************************************************/
|
||||
|
@ -18,7 +18,7 @@ int main(int argListSize, const char *argList[])
|
||||
configParse(argListSize, argList);
|
||||
|
||||
// Display version
|
||||
if (cfgCommand() == cfgCmdVersion)
|
||||
if (!cfgCommandHelp() && cfgCommand() == cfgCmdVersion)
|
||||
{
|
||||
printf(PGBACKREST_NAME " " PGBACKREST_VERSION "\n");
|
||||
fflush(stdout);
|
||||
@ -26,11 +26,11 @@ int main(int argListSize, const char *argList[])
|
||||
}
|
||||
|
||||
// Execute Perl for commands not implemented in C
|
||||
perlExec(perlCommand(argListSize, argList));
|
||||
perlExec(perlCommand());
|
||||
}
|
||||
CATCH_ANY()
|
||||
{
|
||||
fprintf(stderr, "ERROR: [%03d]: %s\n", errorCode(), errorMessage());
|
||||
fprintf(stderr, "ERROR [%03d]: %s\n", errorCode(), errorMessage());
|
||||
fflush(stderr);
|
||||
exit(errorCode());
|
||||
}
|
||||
|
105
src/perl/exec.c
105
src/perl/exec.c
@ -11,7 +11,7 @@ Execute Perl for Legacy Functionality
|
||||
#include "common/error.h"
|
||||
#include "common/memContext.h"
|
||||
#include "common/type.h"
|
||||
#include "config/parse.h"
|
||||
#include "config/config.h"
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Constants used to build perl options
|
||||
@ -27,10 +27,8 @@ Constants used to build perl options
|
||||
/***********************************************************************************************************************************
|
||||
Build list of perl options to use for exec
|
||||
***********************************************************************************************************************************/
|
||||
StringList *perlCommand(int argListSize, const char *argList[])
|
||||
StringList *perlCommand()
|
||||
{
|
||||
ParseData *parseData = configParseArg(argListSize, argList);
|
||||
|
||||
// Begin arg list for perl exec
|
||||
StringList *perlArgList = strLstNew();
|
||||
strLstAdd(perlArgList, strNew(ENV_EXE));
|
||||
@ -41,52 +39,107 @@ StringList *perlCommand(int argListSize, const char *argList[])
|
||||
|
||||
for (ConfigOption optionId = 0; optionId < CFG_OPTION_TOTAL; optionId++)
|
||||
{
|
||||
ParseOption *option = &parseData->parseOptionList[optionId];
|
||||
// Skip the option if it is not valid or not a command line option
|
||||
if (!cfgOptionValid(optionId) || cfgOptionSource(optionId) != cfgSourceParam)
|
||||
continue;
|
||||
|
||||
// If option was found
|
||||
if (option->found)
|
||||
{
|
||||
// If option was negated
|
||||
if (option->negate)
|
||||
strCatFmt(mainCallParam, ", '--no-%s\'", cfgOptionName(optionId));
|
||||
// Else option with no arguments
|
||||
else if (option->valueList == NULL)
|
||||
strCatFmt(mainCallParam, ", '--%s'", cfgOptionName(optionId));
|
||||
// Else options with arguments
|
||||
if (cfgOptionNegate(optionId))
|
||||
strCatFmt(mainCallParam, ", '--no-%s'", cfgOptionName(optionId));
|
||||
// Else not negated
|
||||
else
|
||||
{
|
||||
for (unsigned int argIdx = 0; argIdx < strLstSize(option->valueList); argIdx++)
|
||||
switch (cfgDefOptionType(cfgOptionDefIdFromId(optionId)))
|
||||
{
|
||||
case cfgDefOptTypeBoolean:
|
||||
{
|
||||
strCatFmt(mainCallParam, ", '--%s'", cfgOptionName(optionId));
|
||||
strCatFmt(mainCallParam, ", '%s'", strPtr(strLstGet(option->valueList, argIdx)));
|
||||
break;
|
||||
}
|
||||
|
||||
case cfgDefOptTypeFloat:
|
||||
{
|
||||
strCatFmt(mainCallParam, ", '--%s'", cfgOptionName(optionId));
|
||||
strCatFmt(mainCallParam, ", '%lg'", cfgOptionDbl(optionId));
|
||||
break;
|
||||
}
|
||||
|
||||
case cfgDefOptTypeHash:
|
||||
{
|
||||
const KeyValue *valueKv = cfgOptionKv(optionId);
|
||||
const VariantList *keyList = kvKeyList(valueKv);
|
||||
|
||||
for (unsigned int listIdx = 0; listIdx < varLstSize(keyList); listIdx++)
|
||||
{
|
||||
strCatFmt(mainCallParam, ", '--%s'", cfgOptionName(optionId));
|
||||
strCatFmt(
|
||||
mainCallParam, ", '%s=%s'", strPtr(varStr(varLstGet(keyList, listIdx))),
|
||||
strPtr(varStr(kvGet(valueKv, varLstGet(keyList, listIdx)))));
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case cfgDefOptTypeInteger:
|
||||
{
|
||||
strCatFmt(mainCallParam, ", '--%s'", cfgOptionName(optionId));
|
||||
strCatFmt(mainCallParam, ", '%d'", cfgOptionInt(optionId));
|
||||
break;
|
||||
}
|
||||
|
||||
case cfgDefOptTypeList:
|
||||
{
|
||||
StringList *valueList = strLstNewVarLst(cfgOptionLst(optionId));
|
||||
|
||||
for (unsigned int listIdx = 0; listIdx < strLstSize(valueList); listIdx++)
|
||||
{
|
||||
strCatFmt(mainCallParam, ", '--%s'", cfgOptionName(optionId));
|
||||
strCatFmt(mainCallParam, ", '%s'", strPtr(strLstGet(valueList, listIdx)));
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case cfgDefOptTypeString:
|
||||
{
|
||||
strCatFmt(mainCallParam, ", '--%s'", cfgOptionName(optionId));
|
||||
strCatFmt(mainCallParam, ", '%s'", strPtr(cfgOptionStr(optionId)));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add help command if it was set
|
||||
if (cfgCommandHelp())
|
||||
strCatFmt(mainCallParam, ", '%s'", cfgCommandName(cfgCmdHelp));
|
||||
|
||||
// Add command to pass to main
|
||||
strCatFmt(mainCallParam, ", '%s'", cfgCommandName(parseData->command));
|
||||
if (cfgCommand() != cfgCmdNone && cfgCommand() != cfgCmdHelp)
|
||||
strCatFmt(mainCallParam, ", '%s'", cfgCommandName(cfgCommand()));
|
||||
|
||||
// Add command arguments to pass to main
|
||||
if (parseData->commandArgList != NULL)
|
||||
for (unsigned int argIdx = 0; argIdx < strLstSize(parseData->commandArgList); argIdx++)
|
||||
strCatFmt(mainCallParam, ", '%s'", strPtr(strLstGet(parseData->commandArgList, argIdx)));
|
||||
for (unsigned int paramIdx = 0; paramIdx < strLstSize(cfgCommandParam()); paramIdx++)
|
||||
strCatFmt(mainCallParam, ", '%s'", strPtr(strLstGet(cfgCommandParam(), paramIdx)));
|
||||
|
||||
// Construct perl option list to add to bin
|
||||
StringList *perlOptionList = strLstNewVarLst(cfgOptionLst(cfgOptPerlOption));
|
||||
String *binPerlOption = strNew("");
|
||||
|
||||
if (parseData->perlOptionList != NULL)
|
||||
for (unsigned int argIdx = 0; argIdx < strLstSize(parseData->perlOptionList); argIdx++)
|
||||
if (perlOptionList != NULL)
|
||||
{
|
||||
// Add to bin option list
|
||||
strCatFmt(binPerlOption, " --" PARAM_PERL_OPTION "=\"%s\"", strPtr(strLstGet(parseData->perlOptionList, argIdx)));
|
||||
for (unsigned int argIdx = 0; argIdx < strLstSize(perlOptionList); argIdx++)
|
||||
{
|
||||
// // Add to bin option list
|
||||
// strCatFmt(binPerlOption, " --" PARAM_PERL_OPTION "=\"%s\"", strPtr(strLstGet(perlOptionList, argIdx)));
|
||||
|
||||
// Add to list that will be passed to exec
|
||||
strLstAdd(perlArgList, strLstGet(parseData->perlOptionList, argIdx));
|
||||
strLstAdd(perlArgList, strLstGet(perlOptionList, argIdx));
|
||||
}
|
||||
}
|
||||
|
||||
// Construct Perl main call
|
||||
String *mainCall = strNewFmt(PGBACKREST_MAIN "('%s%s'%s)", argList[0], strPtr(binPerlOption), strPtr(mainCallParam));
|
||||
String *mainCall = strNewFmt(PGBACKREST_MAIN "('%s%s'%s)", strPtr(cfgExe()), strPtr(binPerlOption), strPtr(mainCallParam));
|
||||
|
||||
// End arg list for perl exec
|
||||
strLstAdd(perlArgList, strNew("-M" PGBACKREST_MODULE));
|
||||
|
@ -9,7 +9,7 @@ Execute Perl for Legacy Functionality
|
||||
/***********************************************************************************************************************************
|
||||
Functions
|
||||
***********************************************************************************************************************************/
|
||||
StringList *perlCommand(int argListSize, const char *argList[]);
|
||||
StringList *perlCommand();
|
||||
void perlExec(StringList *perlArgList);
|
||||
|
||||
#endif
|
||||
|
@ -440,7 +440,7 @@ full backup - invalid cmd line (db-master host)
|
||||
> [CONTAINER-EXEC] db-master [BACKREST-BIN] --config=[TEST_PATH]/db-master/pgbackrest.conf --no-online --type=full --stanza=bogus backup
|
||||
------------------------------------------------------------------------------------------------------------------------------------
|
||||
STDERR:
|
||||
ERROR [037]: : backup command requires option: db1-path
|
||||
ERROR [037]: backup command requires option: db1-path
|
||||
HINT: does this stanza exist?
|
||||
|
||||
stop all stanzas (db-master host)
|
||||
|
@ -394,7 +394,7 @@ my $oTestDef =
|
||||
},
|
||||
{
|
||||
&TESTDEF_NAME => 'parse',
|
||||
&TESTDEF_TOTAL => 2,
|
||||
&TESTDEF_TOTAL => 1,
|
||||
&TESTDEF_C => true,
|
||||
|
||||
&TESTDEF_COVERAGE =>
|
||||
|
@ -71,8 +71,86 @@ void testRun()
|
||||
// -----------------------------------------------------------------------------------------------------------------------------
|
||||
if (testBegin("configuration"))
|
||||
{
|
||||
TEST_RESULT_VOID(cfgInit(), "config init");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
TEST_RESULT_INT(cfgCommand(), cfgCmdNone, "command begins as none");
|
||||
TEST_RESULT_VOID(cfgCommandSet(cfgCmdBackup), "command set to backup");
|
||||
TEST_RESULT_INT(cfgCommand(), cfgCmdBackup, "command is backup");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
TEST_RESULT_BOOL(cfgCommandHelp(), false, "command help defaults to false");
|
||||
TEST_RESULT_VOID(cfgCommandHelpSet(true), "set command help");
|
||||
TEST_RESULT_BOOL(cfgCommandHelp(), true, "command help is set");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
TEST_RESULT_INT(strLstSize(cfgCommandParam()), 0, "command param list defaults to empty");
|
||||
TEST_RESULT_VOID(cfgCommandParamSet(strLstAddZ(strLstNew(), "param")), "set command param list");
|
||||
TEST_RESULT_INT(strLstSize(cfgCommandParam()), 1, "command param list is set");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
TEST_RESULT_PTR(cfgExe(), NULL, "exe defaults to null");
|
||||
TEST_RESULT_VOID(cfgExeSet(strNew("/path/to/exe")), "set exe");
|
||||
TEST_RESULT_STR(strPtr(cfgExe()), "/path/to/exe", "exe is set");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
TEST_RESULT_BOOL(cfgOptionNegate(cfgOptConfig), false, "negate defaults to false");
|
||||
TEST_RESULT_VOID(cfgOptionNegateSet(cfgOptConfig, true), "set negate");
|
||||
TEST_RESULT_BOOL(cfgOptionNegate(cfgOptConfig), true, "negate is set");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
TEST_RESULT_BOOL(cfgOptionValid(cfgOptConfig), false, "valid defaults to false");
|
||||
TEST_RESULT_VOID(cfgOptionValidSet(cfgOptConfig, true), "set valid");
|
||||
TEST_RESULT_BOOL(cfgOptionValid(cfgOptConfig), true, "valid is set");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
TEST_RESULT_PTR(cfgOption(cfgOptOnline), NULL, "online is null");
|
||||
TEST_RESULT_VOID(cfgOptionSet(cfgOptOnline, cfgSourceParam, varNewBool(false)), "set online");
|
||||
TEST_RESULT_BOOL(cfgOptionBool(cfgOptOnline), false, "online is set");
|
||||
TEST_RESULT_VOID(cfgOptionSet(cfgOptOnline, cfgSourceParam, varNewStrZ("1")), "set online");
|
||||
TEST_RESULT_BOOL(cfgOptionBool(cfgOptOnline), true, "online is set");
|
||||
TEST_RESULT_INT(cfgOptionSource(cfgOptOnline), cfgSourceParam, "online source is set");
|
||||
TEST_ERROR(cfgOptionDbl(cfgOptOnline), AssertError, "option 'online' is not type 'double'");
|
||||
|
||||
TEST_RESULT_VOID(cfgOptionSet(cfgOptCompressLevel, cfgSourceParam, varNewInt(1)), "set compress-level");
|
||||
TEST_RESULT_INT(cfgOptionInt(cfgOptCompressLevel), 1, "compress-level is set");
|
||||
TEST_RESULT_VOID(cfgOptionSet(cfgOptCompressLevel, cfgSourceDefault, varNewStrZ("3")), "set compress-level");
|
||||
TEST_RESULT_INT(cfgOptionInt(cfgOptCompressLevel), 3, "compress-level is set");
|
||||
TEST_RESULT_INT(cfgOptionSource(cfgOptCompressLevel), cfgSourceDefault, "compress source is set");
|
||||
TEST_ERROR(cfgOptionBool(cfgOptCompressLevel), AssertError, "option 'compress-level' is not type 'bool'");
|
||||
|
||||
TEST_RESULT_VOID(cfgOptionSet(cfgOptProtocolTimeout, cfgSourceParam, varNewDbl(1.1)), "set protocol-timeout");
|
||||
TEST_RESULT_DOUBLE(cfgOptionDbl(cfgOptProtocolTimeout), 1.1, "protocol-timeout is set");
|
||||
TEST_RESULT_VOID(cfgOptionSet(cfgOptProtocolTimeout, cfgSourceConfig, varNewStrZ("3.3")), "set protocol-timeout");
|
||||
TEST_RESULT_DOUBLE(cfgOptionDbl(cfgOptProtocolTimeout), 3.3, "protocol-timeout is set");
|
||||
TEST_RESULT_INT(cfgOptionSource(cfgOptProtocolTimeout), cfgSourceConfig, "protocol-timeout source is set");
|
||||
TEST_ERROR(cfgOptionKv(cfgOptProtocolTimeout), AssertError, "option 'protocol-timeout' is not type 'KeyValue'");
|
||||
|
||||
TEST_ERROR(
|
||||
cfgOptionSet(cfgOptRecoveryOption, cfgSourceParam, varNewDbl(1.1)), AssertError,
|
||||
"option 'recovery-option' must be set with KeyValue variant");
|
||||
TEST_RESULT_VOID(cfgOptionSet(cfgOptRecoveryOption, cfgSourceConfig, varNewKv()), "set recovery-option");
|
||||
TEST_RESULT_INT(varLstSize(kvKeyList(cfgOptionKv(cfgOptRecoveryOption))), 0, "recovery-option is set");
|
||||
TEST_ERROR(cfgOptionLst(cfgOptRecoveryOption), AssertError, "option 'recovery-option' is not type 'VariantList'");
|
||||
|
||||
TEST_RESULT_INT(varLstSize(cfgOptionLst(cfgOptDbInclude)), 0, "db-include defaults to empty");
|
||||
TEST_ERROR(
|
||||
cfgOptionSet(cfgOptDbInclude, cfgSourceParam, varNewDbl(1.1)), AssertError,
|
||||
"option 'db-include' must be set with VariantList variant");
|
||||
TEST_RESULT_VOID(cfgOptionSet(cfgOptDbInclude, cfgSourceConfig, varNewVarLstEmpty()), "set db-include");
|
||||
TEST_RESULT_INT(varLstSize(cfgOptionLst(cfgOptDbInclude)), 0, "db-include is set");
|
||||
TEST_ERROR(cfgOptionStr(cfgOptDbInclude), AssertError, "option 'db-include' is not type 'String'");
|
||||
|
||||
TEST_RESULT_PTR(cfgOptionStr(cfgOptStanza), NULL, "stanza defaults to null");
|
||||
TEST_ERROR(
|
||||
cfgOptionSet(cfgOptStanza, cfgSourceParam, varNewDbl(1.1)), AssertError,
|
||||
"option 'stanza' must be set with String variant");
|
||||
TEST_RESULT_VOID(cfgOptionSet(cfgOptStanza, cfgSourceConfig, varNewStrZ("db")), "set stanza");
|
||||
TEST_RESULT_STR(strPtr(cfgOptionStr(cfgOptStanza)), "db", "stanza is set");
|
||||
TEST_ERROR(cfgOptionInt(cfgOptStanza), AssertError, "option 'stanza' is not type 'int'");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
TEST_RESULT_VOID(cfgInit(), "config init resets value");
|
||||
TEST_RESULT_INT(cfgCommand(), cfgCmdNone, "command begins as none");
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
/***********************************************************************************************************************************
|
||||
Test Configuration Parse
|
||||
***********************************************************************************************************************************/
|
||||
|
||||
#define TEST_BACKREST_EXE "pgbackrest"
|
||||
|
||||
#define TEST_COMMAND_ARCHIVE_GET "archive-get"
|
||||
@ -16,71 +15,37 @@ Test run
|
||||
void testRun()
|
||||
{
|
||||
// -----------------------------------------------------------------------------------------------------------------------------
|
||||
if (testBegin("configParseArg()"))
|
||||
if (testBegin("configParse()"))
|
||||
{
|
||||
ParseData *parseData = NULL;
|
||||
|
||||
StringList *argList = strLstNew();
|
||||
strLstAdd(argList, strNew(TEST_BACKREST_EXE));
|
||||
TEST_ASSIGN(parseData, configParseArg(strLstSize(argList), strLstPtr(argList)), "no command, no args - set help command");
|
||||
TEST_RESULT_INT(parseData->command, cfgCmdHelp, " command is " TEST_COMMAND_HELP);
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
strLstAdd(argList, strNew("--online"));
|
||||
TEST_ERROR(configParseArg(strLstSize(argList), strLstPtr(argList)), CommandRequiredError, "no command found");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
strLstAdd(argList, strNew(TEST_COMMAND_VERSION));
|
||||
TEST_ASSIGN(parseData, configParseArg(strLstSize(argList), strLstPtr(argList)), TEST_COMMAND_VERSION " command");
|
||||
TEST_RESULT_INT(parseData->command, cfgCmdVersion, " command is " TEST_COMMAND_VERSION);
|
||||
TEST_RESULT_PTR(parseData->perlOptionList, NULL, " no perl options");
|
||||
TEST_RESULT_PTR(parseData->commandArgList, NULL, " no command arguments");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
strLstAdd(argList, strNew("--perl-option=value"));
|
||||
TEST_ASSIGN(parseData, configParseArg(strLstSize(argList), strLstPtr(argList)), "perl option");
|
||||
TEST_RESULT_STR(strPtr(strLstJoin(parseData->perlOptionList, ",")), "value", "check perl option");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
strLstAdd(argList, strNewFmt("--%s", cfgOptionName(cfgOptDelta)));
|
||||
TEST_ASSIGN(parseData, configParseArg(strLstSize(argList), strLstPtr(argList)), "delta option");
|
||||
TEST_RESULT_BOOL(parseData->parseOptionList[cfgOptDelta].found, true, " option found");
|
||||
TEST_RESULT_BOOL(parseData->parseOptionList[cfgOptDelta].negate, false, " option not negated");
|
||||
TEST_RESULT_PTR(parseData->parseOptionList[cfgOptDelta].valueList, NULL, " no values");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
strLstAdd(argList, strNew("--" BOGUS_STR));
|
||||
TEST_ERROR(configParseArg(strLstSize(argList), strLstPtr(argList)), OptionInvalidError, "invalid option '--BOGUS'");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
argList = strLstNew();
|
||||
strLstAdd(argList, strNew(TEST_BACKREST_EXE));
|
||||
strLstAdd(argList, strNew("--no-archive-check"));
|
||||
strLstAdd(argList, strNew(TEST_COMMAND_HELP));
|
||||
TEST_ASSIGN(parseData, configParseArg(strLstSize(argList), strLstPtr(argList)), TEST_COMMAND_HELP " command");
|
||||
TEST_RESULT_BOOL(parseData->parseOptionList[cfgOptArchiveCheck].negate, true, " option negated");
|
||||
StringList *argList = NULL;
|
||||
String *configFile = strNewFmt("%s/test.config", testPath());
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
argList = strLstNew();
|
||||
strLstAdd(argList, strNew(TEST_BACKREST_EXE));
|
||||
strLstAdd(argList, strNew(BOGUS_STR));
|
||||
TEST_ERROR(configParseArg(strLstSize(argList), strLstPtr(argList)), CommandInvalidError, "invalid command 'BOGUS'");
|
||||
TEST_ERROR(configParse(strLstSize(argList), strLstPtr(argList)), CommandInvalidError, "invalid command 'BOGUS'");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
argList = strLstNew();
|
||||
strLstAdd(argList, strNew(TEST_BACKREST_EXE));
|
||||
strLstAdd(argList, strNew("--no-online"));
|
||||
strLstAdd(argList, strNew("--no-online"));
|
||||
TEST_ERROR(
|
||||
configParseArg(strLstSize(argList), strLstPtr(argList)), OptionInvalidError,
|
||||
"option 'online' is negated multiple times");
|
||||
strLstAdd(argList, strNew("--" BOGUS_STR));
|
||||
TEST_ERROR(configParse(strLstSize(argList), strLstPtr(argList)), OptionInvalidError, "invalid option '--BOGUS'");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
argList = strLstNew();
|
||||
strLstAdd(argList, strNew(TEST_BACKREST_EXE));
|
||||
strLstAdd(argList, strNew("--db-host"));
|
||||
TEST_ERROR(
|
||||
configParseArg(strLstSize(argList), strLstPtr(argList)), OptionInvalidError, "option '--db-host' requires argument");
|
||||
configParse(strLstSize(argList), strLstPtr(argList)), OptionInvalidError, "option '--db-host' requires argument");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
argList = strLstNew();
|
||||
strLstAdd(argList, strNew(TEST_BACKREST_EXE));
|
||||
strLstAdd(argList, strNew("--no-online"));
|
||||
strLstAdd(argList, strNew("--no-online"));
|
||||
TEST_ERROR(
|
||||
configParse(strLstSize(argList), strLstPtr(argList)), OptionInvalidError, "option 'online' is negated multiple times");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
argList = strLstNew();
|
||||
@ -88,8 +53,7 @@ void testRun()
|
||||
strLstAdd(argList, strNew("--config=/etc/config"));
|
||||
strLstAdd(argList, strNew("--no-config"));
|
||||
TEST_ERROR(
|
||||
configParseArg(strLstSize(argList), strLstPtr(argList)), OptionInvalidError,
|
||||
"option 'config' cannot be set and negated");
|
||||
configParse(strLstSize(argList), strLstPtr(argList)), OptionInvalidError, "option 'config' cannot be set and negated");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
argList = strLstNew();
|
||||
@ -97,48 +61,362 @@ void testRun()
|
||||
strLstAdd(argList, strNew("--compress-level=3"));
|
||||
strLstAdd(argList, strNew("--compress-level=3"));
|
||||
TEST_ERROR(
|
||||
configParseArg(strLstSize(argList), strLstPtr(argList)), OptionInvalidError,
|
||||
configParse(strLstSize(argList), strLstPtr(argList)), OptionInvalidError,
|
||||
"option 'compress-level' cannot have multiple arguments");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
argList = strLstNew();
|
||||
strLstAdd(argList, strNew(TEST_BACKREST_EXE));
|
||||
strLstAdd(argList, strNew("--online"));
|
||||
TEST_ERROR(configParse(strLstSize(argList), strLstPtr(argList)), CommandRequiredError, "no command found");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
argList = strLstNew();
|
||||
strLstAdd(argList, strNew(TEST_BACKREST_EXE));
|
||||
strLstAdd(argList, strNew(TEST_COMMAND_BACKUP));
|
||||
TEST_ERROR(
|
||||
configParse(strLstSize(argList), strLstPtr(argList)), OptionRequiredError,
|
||||
"backup command requires option: db1-path\nHINT: does this stanza exist?");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
argList = strLstNew();
|
||||
strLstAdd(argList, strNew(TEST_BACKREST_EXE));
|
||||
strLstAdd(argList, strNew("--db-path=/path/to/db"));
|
||||
strLstAdd(argList, strNew(TEST_COMMAND_BACKUP));
|
||||
TEST_ERROR(
|
||||
configParse(strLstSize(argList), strLstPtr(argList)), OptionRequiredError,
|
||||
"backup command requires option: stanza");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
argList = strLstNew();
|
||||
strLstAdd(argList, strNew(TEST_BACKREST_EXE));
|
||||
strLstAdd(argList, strNew("--db-path=/path/to/db"));
|
||||
strLstAdd(argList, strNew("--no-config"));
|
||||
strLstAdd(argList, strNew("--stanza=db"));
|
||||
strLstAdd(argList, strNew("--repo-s3-key=xxx"));
|
||||
strLstAdd(argList, strNew(TEST_COMMAND_BACKUP));
|
||||
TEST_ERROR(
|
||||
configParse(strLstSize(argList), strLstPtr(argList)), OptionInvalidError,
|
||||
"option 'repo-s3-key' not valid without option 'repo-type' = 's3'");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
argList = strLstNew();
|
||||
strLstAdd(argList, strNew(TEST_BACKREST_EXE));
|
||||
strLstAdd(argList, strNew("--db-path=/path/to/db"));
|
||||
strLstAdd(argList, strNew("--backup-user=xxx"));
|
||||
strLstAdd(argList, strNew("--stanza=db"));
|
||||
strLstAdd(argList, strNew(TEST_COMMAND_BACKUP));
|
||||
TEST_ERROR(
|
||||
configParse(strLstSize(argList), strLstPtr(argList)), OptionInvalidError,
|
||||
"option 'backup-user' not valid without option 'backup-host'");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
argList = strLstNew();
|
||||
strLstAdd(argList, strNew(TEST_BACKREST_EXE));
|
||||
strLstAdd(argList, strNew("--db-path=/path/to/db"));
|
||||
strLstAdd(argList, strNew("--stanza=db"));
|
||||
strLstAdd(argList, strNew("--force"));
|
||||
strLstAdd(argList, strNew(TEST_COMMAND_BACKUP));
|
||||
TEST_ERROR(
|
||||
configParse(strLstSize(argList), strLstPtr(argList)), OptionInvalidError,
|
||||
"option 'force' not valid without option 'no-online'");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
argList = strLstNew();
|
||||
strLstAdd(argList, strNew(TEST_BACKREST_EXE));
|
||||
strLstAdd(argList, strNew("--db-path=/path/to/db"));
|
||||
strLstAdd(argList, strNew("--stanza=db"));
|
||||
strLstAdd(argList, strNew("--test-delay=1"));
|
||||
strLstAdd(argList, strNew(TEST_COMMAND_BACKUP));
|
||||
TEST_ERROR(
|
||||
configParse(strLstSize(argList), strLstPtr(argList)), OptionInvalidError,
|
||||
"option 'test-delay' not valid without option 'test'");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
argList = strLstNew();
|
||||
strLstAdd(argList, strNew(TEST_BACKREST_EXE));
|
||||
strLstAdd(argList, strNew("--db-path=/path/to/db"));
|
||||
strLstAdd(argList, strNew("--stanza=db"));
|
||||
strLstAdd(argList, strNew("--recovery-option=a=b"));
|
||||
strLstAdd(argList, strNew(TEST_COMMAND_BACKUP));
|
||||
TEST_ERROR(
|
||||
configParse(strLstSize(argList), strLstPtr(argList)), OptionInvalidError,
|
||||
"option 'recovery-option' not valid for command 'backup'");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
argList = strLstNew();
|
||||
strLstAdd(argList, strNew(TEST_BACKREST_EXE));
|
||||
strLstAdd(argList, strNew("--db-path=/path/to/db"));
|
||||
strLstAdd(argList, strNew("--stanza=db"));
|
||||
strLstAdd(argList, strNew("--target-exclusive"));
|
||||
strLstAdd(argList, strNew(TEST_COMMAND_RESTORE));
|
||||
TEST_ERROR(
|
||||
configParse(strLstSize(argList), strLstPtr(argList)), OptionInvalidError,
|
||||
"option 'target-exclusive' not valid without option 'type' in ('time', 'xid')");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
argList = strLstNew();
|
||||
strLstAdd(argList, strNew(TEST_BACKREST_EXE));
|
||||
strLstAdd(argList, strNew("--db-path=/path/to/db"));
|
||||
strLstAdd(argList, strNew("--stanza=db"));
|
||||
strLstAdd(argList, strNew("--type=bogus"));
|
||||
strLstAdd(argList, strNew(TEST_COMMAND_RESTORE));
|
||||
TEST_ERROR(
|
||||
configParse(strLstSize(argList), strLstPtr(argList)), OptionInvalidValueError,
|
||||
"'bogus' is not valid for 'type' option");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
argList = strLstNew();
|
||||
strLstAdd(argList, strNew(TEST_BACKREST_EXE));
|
||||
strLstAdd(argList, strNew("--db-path=/path/to/db"));
|
||||
strLstAdd(argList, strNew("--stanza=db"));
|
||||
strLstAdd(argList, strNew("--process-max=0"));
|
||||
strLstAdd(argList, strNew(TEST_COMMAND_RESTORE));
|
||||
TEST_ERROR(
|
||||
configParse(strLstSize(argList), strLstPtr(argList)), OptionInvalidValueError,
|
||||
"'0' is not valid for 'process-max' option");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
argList = strLstNew();
|
||||
strLstAdd(argList, strNew(TEST_BACKREST_EXE));
|
||||
strLstAdd(argList, strNew("--db-path=/path/to/db"));
|
||||
strLstAdd(argList, strNew("--stanza=db"));
|
||||
strLstAdd(argList, strNew("--process-max=bogus"));
|
||||
strLstAdd(argList, strNew(TEST_COMMAND_RESTORE));
|
||||
TEST_ERROR(
|
||||
configParse(strLstSize(argList), strLstPtr(argList)), OptionInvalidValueError,
|
||||
"'bogus' is not valid for 'process-max' option");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
argList = strLstNew();
|
||||
strLstAdd(argList, strNew(TEST_BACKREST_EXE));
|
||||
strLstAdd(argList, strNew("--db-path=/path/to/db"));
|
||||
strLstAdd(argList, strNew("--stanza=db"));
|
||||
strLstAdd(argList, strNew("--protocol-timeout=.01"));
|
||||
strLstAdd(argList, strNew(TEST_COMMAND_RESTORE));
|
||||
TEST_ERROR(
|
||||
configParse(strLstSize(argList), strLstPtr(argList)), OptionInvalidValueError,
|
||||
"'.01' is not valid for 'protocol-timeout' option");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
argList = strLstNew();
|
||||
strLstAdd(argList, strNew(TEST_BACKREST_EXE));
|
||||
strLstAdd(argList, strNew("--db-path=/path/to/db"));
|
||||
strLstAdd(argList, strNew("--stanza=db"));
|
||||
strLstAdd(argList, strNew("--protocol-timeout=bogus"));
|
||||
strLstAdd(argList, strNew(TEST_COMMAND_RESTORE));
|
||||
TEST_ERROR(
|
||||
configParse(strLstSize(argList), strLstPtr(argList)), OptionInvalidValueError,
|
||||
"'bogus' is not valid for 'protocol-timeout' option");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
argList = strLstNew();
|
||||
strLstAdd(argList, strNew(TEST_BACKREST_EXE));
|
||||
strLstAdd(argList, strNew("--stanza=db"));
|
||||
strLstAdd(argList, strNewFmt("--config=%s", strPtr(configFile)));
|
||||
strLstAdd(argList, strNew(TEST_COMMAND_BACKUP));
|
||||
|
||||
storagePut(storageLocal(), configFile, bufNewStr(strNew(
|
||||
"[global]\n"
|
||||
"compress=bogus\n"
|
||||
)));
|
||||
|
||||
TEST_ERROR(configParse(
|
||||
strLstSize(argList), strLstPtr(argList)), OptionInvalidError, "boolean option 'compress' must be 'y' or 'n'");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
argList = strLstNew();
|
||||
strLstAdd(argList, strNew(TEST_BACKREST_EXE));
|
||||
strLstAdd(argList, strNew("--stanza=db"));
|
||||
strLstAdd(argList, strNewFmt("--config=%s", strPtr(configFile)));
|
||||
strLstAdd(argList, strNew(TEST_COMMAND_BACKUP));
|
||||
|
||||
storagePut(storageLocal(), configFile, bufNewStr(strNew(
|
||||
"[global]\n"
|
||||
"compress=\n"
|
||||
)));
|
||||
|
||||
TEST_ERROR(configParse(
|
||||
strLstSize(argList), strLstPtr(argList)), OptionInvalidValueError,
|
||||
"section 'global', key 'compress' must have a value");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
argList = strLstNew();
|
||||
strLstAdd(argList, strNew(TEST_BACKREST_EXE));
|
||||
|
||||
TEST_RESULT_VOID(configParse(strLstSize(argList), strLstPtr(argList)), "no command");
|
||||
TEST_RESULT_BOOL(cfgCommandHelp(), true, " help is set");
|
||||
TEST_RESULT_INT(cfgCommand(), cfgCmdNone, " command is none");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
argList = strLstNew();
|
||||
strLstAdd(argList, strNew(TEST_BACKREST_EXE));
|
||||
strLstAdd(argList, strNew("help"));
|
||||
|
||||
TEST_RESULT_VOID(configParse(strLstSize(argList), strLstPtr(argList)), "help command");
|
||||
TEST_RESULT_BOOL(cfgCommandHelp(), true, " help is set");
|
||||
TEST_RESULT_INT(cfgCommand(), cfgCmdHelp, " command is help");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
argList = strLstNew();
|
||||
strLstAdd(argList, strNew(TEST_BACKREST_EXE));
|
||||
strLstAdd(argList, strNew("help"));
|
||||
strLstAdd(argList, strNew("version"));
|
||||
|
||||
TEST_RESULT_VOID(configParse(strLstSize(argList), strLstPtr(argList)), "help for version command");
|
||||
TEST_RESULT_BOOL(cfgCommandHelp(), true, " help is set");
|
||||
TEST_RESULT_INT(cfgCommand(), cfgCmdVersion, " command is version");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
argList = strLstNew();
|
||||
strLstAdd(argList, strNew(TEST_BACKREST_EXE));
|
||||
strLstAdd(argList, strNew("--stanza=db"));
|
||||
strLstAdd(argList, strNew(TEST_COMMAND_ARCHIVE_GET));
|
||||
strLstAdd(argList, strNew("000000010000000200000003"));
|
||||
strLstAdd(argList, strNew("/path/to/wal/RECOVERYWAL"));
|
||||
TEST_ASSIGN(parseData, configParseArg(strLstSize(argList), strLstPtr(argList)), "command arguments");
|
||||
TEST_RESULT_VOID(configParse(strLstSize(argList), strLstPtr(argList)), "command arguments");
|
||||
TEST_RESULT_STR(
|
||||
strPtr(strLstJoin(parseData->commandArgList, "|")), "000000010000000200000003|/path/to/wal/RECOVERYWAL",
|
||||
strPtr(strLstJoin(cfgCommandParam(), "|")), "000000010000000200000003|/path/to/wal/RECOVERYWAL",
|
||||
" check command arguments");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
argList = strLstNew();
|
||||
strLstAdd(argList, strNew(TEST_BACKREST_EXE));
|
||||
strLstAdd(argList, strNew("--db-host=db1.test.org"));
|
||||
strLstAdd(argList, strNew("--stanza=db"));
|
||||
strLstAdd(argList, strNew("--backup-host=backup"));
|
||||
strLstAdd(argList, strNew("--backup-user=pgbackrest"));
|
||||
strLstAdd(argList, strNew("--db-path=/path/to/db"));
|
||||
strLstAdd(argList, strNew("--no-online"));
|
||||
strLstAdd(argList, strNew(TEST_COMMAND_BACKUP));
|
||||
TEST_ASSIGN(parseData, configParseArg(strLstSize(argList), strLstPtr(argList)), "single valid option");
|
||||
TEST_RESULT_STR(
|
||||
strPtr(strLstJoin(parseData->parseOptionList[cfgOptDbHost].valueList, "|")), "db1.test.org", " check db-host option");
|
||||
TEST_RESULT_VOID(configParse(strLstSize(argList), strLstPtr(argList)), TEST_COMMAND_BACKUP " command");
|
||||
TEST_RESULT_INT(cfgCommand(), cfgCmdBackup, " command is " TEST_COMMAND_BACKUP);
|
||||
|
||||
TEST_RESULT_STR(strPtr(cfgExe()), TEST_BACKREST_EXE, " exe is set");
|
||||
|
||||
TEST_RESULT_STR(strPtr(cfgOptionStr(cfgOptStanza)), "db", " stanza is set");
|
||||
TEST_RESULT_INT(cfgOptionSource(cfgOptStanza), cfgSourceParam, " stanza is source param");
|
||||
TEST_RESULT_STR(strPtr(cfgOptionStr(cfgOptDbPath)), "/path/to/db", " db-path is set");
|
||||
TEST_RESULT_INT(cfgOptionSource(cfgOptDbPath), cfgSourceParam, " db-path is source param");
|
||||
TEST_RESULT_BOOL(cfgOptionBool(cfgOptOnline), false, " online is not set");
|
||||
TEST_RESULT_INT(cfgOptionSource(cfgOptOnline), cfgSourceParam, " online is source default");
|
||||
TEST_RESULT_BOOL(cfgOptionBool(cfgOptCompress), true, " compress is set");
|
||||
TEST_RESULT_INT(cfgOptionSource(cfgOptCompress), cfgSourceDefault, " compress is source default");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
argList = strLstNew();
|
||||
strLstAdd(argList, strNew(TEST_BACKREST_EXE));
|
||||
strLstAdd(argList, strNew("--link-map=ts1=/path/to/ts1"));
|
||||
strLstAdd(argList, strNew("--link-map=ts2=/path/to/ts2"));
|
||||
strLstAdd(argList, strNew(TEST_COMMAND_RESTORE));
|
||||
TEST_ASSIGN(parseData, configParseArg(strLstSize(argList), strLstPtr(argList)), "multiple valid options");
|
||||
TEST_RESULT_STR(
|
||||
strPtr(strLstJoin(parseData->parseOptionList[cfgOptLinkMap].valueList, "|")), "ts1=/path/to/ts1|ts2=/path/to/ts2",
|
||||
" check link-map options");
|
||||
}
|
||||
strLstAdd(argList, strNew("--stanza=db"));
|
||||
strLstAdd(argList, strNewFmt("--config=%s", strPtr(configFile)));
|
||||
strLstAdd(argList, strNew("--no-online"));
|
||||
strLstAdd(argList, strNew("--db1-host=db"));
|
||||
strLstAdd(argList, strNew(TEST_COMMAND_BACKUP));
|
||||
|
||||
// -----------------------------------------------------------------------------------------------------------------------------
|
||||
if (testBegin("configParse()"))
|
||||
{
|
||||
StringList *argList = strLstNew();
|
||||
storagePut(storageLocal(), configFile, bufNewStr(strNew(
|
||||
"[global]\n"
|
||||
"compress-level=3\n"
|
||||
"\n"
|
||||
"[global:backup]\n"
|
||||
"hardlink=y\n"
|
||||
"bogus=bogus\n"
|
||||
"no-compress=y\n"
|
||||
"archive-copy=y\n"
|
||||
"online=y\n"
|
||||
"\n"
|
||||
"[db:backup]\n"
|
||||
"compress=n\n"
|
||||
"recovery-option=a=b\n"
|
||||
"\n"
|
||||
"[db]\n"
|
||||
"db1-host=db\n"
|
||||
"db1-path=/path/to/db\n"
|
||||
"recovery-option=c=d\n"
|
||||
)));
|
||||
|
||||
TEST_RESULT_VOID(configParse(strLstSize(argList), strLstPtr(argList)), TEST_COMMAND_BACKUP " command");
|
||||
|
||||
TEST_RESULT_STR(strPtr(cfgOptionStr(cfgOptDbPath)), "/path/to/db", " db-path is set");
|
||||
TEST_RESULT_INT(cfgOptionSource(cfgOptDbPath), cfgSourceConfig, " db-path is source config");
|
||||
TEST_RESULT_BOOL(cfgOptionBool(cfgOptCompress), false, " compress not is set");
|
||||
TEST_RESULT_INT(cfgOptionSource(cfgOptCompress), cfgSourceConfig, " compress is source config");
|
||||
TEST_RESULT_PTR(cfgOption(cfgOptArchiveCheck), NULL, " archive-check is not set");
|
||||
TEST_RESULT_PTR(cfgOption(cfgOptArchiveCopy), NULL, " archive-copy is not set");
|
||||
TEST_RESULT_BOOL(cfgOptionBool(cfgOptHardlink), true, " hardlink is set");
|
||||
TEST_RESULT_INT(cfgOptionSource(cfgOptHardlink), cfgSourceConfig, " hardlink is source config");
|
||||
TEST_RESULT_INT(cfgOptionInt(cfgOptCompressLevel), 3, " compress-level is set");
|
||||
TEST_RESULT_INT(cfgOptionSource(cfgOptCompressLevel), cfgSourceConfig, " compress-level is source config");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
argList = strLstNew();
|
||||
strLstAdd(argList, strNew(TEST_BACKREST_EXE));
|
||||
strLstAdd(argList, strNew(TEST_COMMAND_HELP));
|
||||
TEST_RESULT_VOID(configParse(strLstSize(argList), strLstPtr(argList)), TEST_COMMAND_HELP " command");
|
||||
TEST_RESULT_INT(cfgCommand(), cfgCmdHelp, " command is " TEST_COMMAND_HELP);
|
||||
strLstAdd(argList, strNew("--db-path=/path/to/db"));
|
||||
strLstAdd(argList, strNew("--recovery-option=a"));
|
||||
strLstAdd(argList, strNew("--stanza=db"));
|
||||
strLstAdd(argList, strNew(TEST_COMMAND_RESTORE));
|
||||
TEST_ERROR(
|
||||
configParse(strLstSize(argList), strLstPtr(argList)), OptionInvalidError,
|
||||
"key/value 'a' not valid for 'recovery-option' option");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
argList = strLstNew();
|
||||
strLstAdd(argList, strNew(TEST_BACKREST_EXE));
|
||||
strLstAdd(argList, strNew("--db-path=/path/to/db"));
|
||||
strLstAdd(argList, strNew("--recovery-option=a"));
|
||||
strLstAdd(argList, strNew("--stanza=db"));
|
||||
strLstAdd(argList, strNew(TEST_COMMAND_RESTORE));
|
||||
TEST_ERROR(
|
||||
configParse(strLstSize(argList), strLstPtr(argList)), OptionInvalidError,
|
||||
"key/value 'a' not valid for 'recovery-option' option");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
argList = strLstNew();
|
||||
strLstAdd(argList, strNew(TEST_BACKREST_EXE));
|
||||
strLstAdd(argList, strNew("--db-path=/path/to/db"));
|
||||
strLstAdd(argList, strNew("--db-include=abc"));
|
||||
strLstAdd(argList, strNew("--db-include=def"));
|
||||
strLstAdd(argList, strNew("--stanza=db"));
|
||||
strLstAdd(argList, strNew(TEST_COMMAND_RESTORE));
|
||||
TEST_RESULT_VOID(configParse(strLstSize(argList), strLstPtr(argList)), TEST_COMMAND_RESTORE " command");
|
||||
|
||||
const VariantList *includeList = NULL;
|
||||
TEST_ASSIGN(includeList, cfgOptionLst(cfgOptDbInclude), "get db include options");
|
||||
TEST_RESULT_STR(strPtr(varStr(varLstGet(includeList, 0))), "abc", "check db include option");
|
||||
TEST_RESULT_STR(strPtr(varStr(varLstGet(includeList, 1))), "def", "check db include option");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
argList = strLstNew();
|
||||
strLstAdd(argList, strNew(TEST_BACKREST_EXE));
|
||||
strLstAdd(argList, strNew("--db-path=/path/to/db"));
|
||||
strLstAdd(argList, strNew("--recovery-option=a=b"));
|
||||
strLstAdd(argList, strNew("--recovery-option=c=de=fg hi"));
|
||||
strLstAdd(argList, strNew("--stanza=db"));
|
||||
strLstAdd(argList, strNew(TEST_COMMAND_RESTORE));
|
||||
TEST_RESULT_VOID(configParse(strLstSize(argList), strLstPtr(argList)), TEST_COMMAND_RESTORE " command");
|
||||
|
||||
const KeyValue *recoveryKv = NULL;
|
||||
TEST_ASSIGN(recoveryKv, cfgOptionKv(cfgOptRecoveryOption), "get recovery options");
|
||||
TEST_RESULT_STR(strPtr(varStr(kvGet(recoveryKv, varNewStr(strNew("a"))))), "b", "check recovery option");
|
||||
TEST_RESULT_STR(strPtr(varStr(kvGet(recoveryKv, varNewStr(strNew("c"))))), "de=fg hi", "check recovery option");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
argList = strLstNew();
|
||||
strLstAdd(argList, strNew(TEST_BACKREST_EXE));
|
||||
strLstAdd(argList, strNewFmt("--config=%s", strPtr(configFile)));
|
||||
strLstAdd(argList, strNew("--stanza=db"));
|
||||
strLstAdd(argList, strNew(TEST_COMMAND_RESTORE));
|
||||
|
||||
storagePut(storageLocal(), configFile, bufNewStr(strNew(
|
||||
"[global:restore]\n"
|
||||
"recovery-option=f=g\n"
|
||||
"recovery-option=hijk=l\n"
|
||||
"\n"
|
||||
"[db]\n"
|
||||
"db1-path=/path/to/db\n"
|
||||
)));
|
||||
|
||||
TEST_RESULT_VOID(configParse(strLstSize(argList), strLstPtr(argList)), TEST_COMMAND_RESTORE " command");
|
||||
|
||||
TEST_ASSIGN(recoveryKv, cfgOptionKv(cfgOptRecoveryOption), "get recovery options");
|
||||
TEST_RESULT_STR(strPtr(varStr(kvGet(recoveryKv, varNewStr(strNew("f"))))), "g", "check recovery option");
|
||||
TEST_RESULT_STR(strPtr(varStr(kvGet(recoveryKv, varNewStr(strNew("hijk"))))), "l", "check recovery option");
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
/***********************************************************************************************************************************
|
||||
Test Perl Exec
|
||||
***********************************************************************************************************************************/
|
||||
#include "config/config.h"
|
||||
|
||||
#define TEST_ENV_EXE "/usr/bin/env"
|
||||
#define TEST_PERL_EXE "perl"
|
||||
@ -17,69 +18,80 @@ void testRun()
|
||||
if (testBegin("perlCommand()"))
|
||||
{
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
const char *cmdLineParam[128];
|
||||
int cmdLineParamSize = 0;
|
||||
|
||||
cmdLineParam[cmdLineParamSize++] = TEST_BACKREST_EXE;
|
||||
cmdLineParam[cmdLineParamSize++] = "backup";
|
||||
cfgInit();
|
||||
cfgCommandSet(cfgCmdInfo);
|
||||
cfgExeSet(strNew(TEST_BACKREST_EXE));
|
||||
|
||||
TEST_RESULT_STR(
|
||||
strPtr(strLstJoin(perlCommand(cmdLineParamSize, cmdLineParam), "|")),
|
||||
TEST_ENV_EXE "|" TEST_PERL_EXE "|" TEST_PERL_MAIN "', 'backup')|[NULL]", "simple command");
|
||||
strPtr(strLstJoin(perlCommand(), "|")),
|
||||
TEST_ENV_EXE "|" TEST_PERL_EXE "|" TEST_PERL_MAIN "', 'info')|[NULL]", "command with no options");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
cmdLineParam[cmdLineParamSize++] = "--compress";
|
||||
cfgInit();
|
||||
cfgCommandSet(cfgCmdBackup);
|
||||
cfgExeSet(strNew(TEST_BACKREST_EXE));
|
||||
|
||||
cfgOptionValidSet(cfgOptCompress, true);
|
||||
cfgOptionSet(cfgOptCompress, cfgSourceParam, varNewBool(true));
|
||||
|
||||
cfgOptionValidSet(cfgOptOnline, true);
|
||||
cfgOptionNegateSet(cfgOptOnline, true);
|
||||
cfgOptionSet(cfgOptOnline, cfgSourceParam, varNewBool(false));
|
||||
|
||||
cfgOptionValidSet(cfgOptProtocolTimeout, true);
|
||||
cfgOptionSet(cfgOptProtocolTimeout, cfgSourceParam, varNewDbl(1.1));
|
||||
|
||||
cfgOptionValidSet(cfgOptCompressLevel, true);
|
||||
cfgOptionSet(cfgOptCompressLevel, cfgSourceParam, varNewInt(3));
|
||||
|
||||
cfgOptionValidSet(cfgOptStanza, true);
|
||||
cfgOptionSet(cfgOptStanza, cfgSourceParam, varNewStr(strNew("db")));
|
||||
|
||||
TEST_RESULT_STR(
|
||||
strPtr(strLstJoin(perlCommand(cmdLineParamSize, cmdLineParam), "|")),
|
||||
TEST_ENV_EXE "|" TEST_PERL_EXE "|" TEST_PERL_MAIN "', '--compress', 'backup')|[NULL]", "simple option");
|
||||
strPtr(strLstJoin(perlCommand(), "|")),
|
||||
TEST_ENV_EXE "|" TEST_PERL_EXE "|" TEST_PERL_MAIN "'"
|
||||
", '--compress', '--compress-level', '3', '--no-online', '--protocol-timeout', '1.1', '--stanza', 'db'"
|
||||
", 'backup')|[NULL]", "simple options");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
cmdLineParam[cmdLineParamSize++] = "--db-host=db1";
|
||||
cfgInit();
|
||||
cfgCommandHelpSet(true);
|
||||
cfgCommandSet(cfgCmdRestore);
|
||||
cfgExeSet(strNew(TEST_BACKREST_EXE));
|
||||
|
||||
cfgOptionValidSet(cfgOptDbInclude, true);
|
||||
StringList *includeList = strLstNew();
|
||||
strLstAdd(includeList, strNew("db1"));
|
||||
strLstAdd(includeList, strNew("db2"));
|
||||
cfgOptionSet(cfgOptDbInclude, cfgSourceParam, varNewVarLst(varLstNewStrLst(includeList)));
|
||||
|
||||
cfgOptionValidSet(cfgOptRecoveryOption, true);
|
||||
// !!! WHY DO WE STILL NEED TO CREATE THE VAR KV EMPTY?
|
||||
Variant *recoveryVar = varNewKv();
|
||||
KeyValue *recoveryKv = varKv(recoveryVar);
|
||||
kvPut(recoveryKv, varNewStr(strNew("standby_mode")), varNewStr(strNew("on")));
|
||||
kvPut(recoveryKv, varNewStr(strNew("primary_conn_info")), varNewStr(strNew("blah")));
|
||||
cfgOptionSet(cfgOptRecoveryOption, cfgSourceParam, recoveryVar);
|
||||
|
||||
StringList *commandParamList = strLstNew();
|
||||
strLstAdd(commandParamList, strNew("param1"));
|
||||
strLstAdd(commandParamList, strNew("param2"));
|
||||
cfgCommandParamSet(commandParamList);
|
||||
|
||||
cfgOptionValidSet(cfgOptPerlOption, true);
|
||||
// !!! WHY DO WE STILL NEED TO CREATE THE VAR KV EMPTY?
|
||||
StringList *perlList = strLstNew();
|
||||
strLstAdd(perlList, strNew("-I."));
|
||||
strLstAdd(perlList, strNew("-MDevel::Cover=-silent,1"));
|
||||
cfgOptionSet(cfgOptPerlOption, cfgSourceParam, varNewVarLst(varLstNewStrLst(perlList)));
|
||||
|
||||
TEST_RESULT_STR(
|
||||
strPtr(strLstJoin(perlCommand(cmdLineParamSize, cmdLineParam), "|")),
|
||||
TEST_ENV_EXE "|" TEST_PERL_EXE "|" TEST_PERL_MAIN "', '--compress', '--db1-host', 'db1', 'backup')|[NULL]",
|
||||
"option with = before value");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
cmdLineParam[cmdLineParamSize++] = "--db-user";
|
||||
cmdLineParam[cmdLineParamSize++] = "postgres";
|
||||
|
||||
TEST_RESULT_STR(
|
||||
strPtr(strLstJoin(perlCommand(cmdLineParamSize, cmdLineParam), "|")),
|
||||
TEST_ENV_EXE "|" TEST_PERL_EXE "|" TEST_PERL_MAIN
|
||||
"', '--compress', '--db1-host', 'db1', '--db1-user', 'postgres', 'backup')|[NULL]",
|
||||
"option with space before value");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
cmdLineParam[cmdLineParamSize++] = "--perl-option=-I.";
|
||||
|
||||
TEST_RESULT_STR(
|
||||
strPtr(strLstJoin(perlCommand(cmdLineParamSize, cmdLineParam), "|")),
|
||||
TEST_ENV_EXE "|" TEST_PERL_EXE "|-I.|" TEST_PERL_MAIN
|
||||
" --perl-option=\"-I.\"', '--compress', '--db1-host', 'db1', '--db1-user', 'postgres', 'backup')|[NULL]",
|
||||
"perl option");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
cmdLineParam[cmdLineParamSize++] = "--no-online";
|
||||
|
||||
TEST_RESULT_STR(
|
||||
strPtr(strLstJoin(perlCommand(cmdLineParamSize, cmdLineParam), "|")),
|
||||
TEST_ENV_EXE "|" TEST_PERL_EXE "|-I.|" TEST_PERL_MAIN
|
||||
" --perl-option=\"-I.\"', '--compress', '--db1-host', 'db1', '--db1-user', 'postgres', '--no-online',"
|
||||
" 'backup')|[NULL]",
|
||||
"perl option");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
cmdLineParam[cmdLineParamSize++] = "cmdarg1";
|
||||
|
||||
TEST_RESULT_STR(
|
||||
strPtr(strLstJoin(perlCommand(cmdLineParamSize, cmdLineParam), "|")),
|
||||
TEST_ENV_EXE "|" TEST_PERL_EXE "|-I.|" TEST_PERL_MAIN
|
||||
" --perl-option=\"-I.\"', '--compress', '--db1-host', 'db1', '--db1-user', 'postgres', '--no-online', 'backup',"
|
||||
" 'cmdarg1')|[NULL]",
|
||||
"perl option");
|
||||
strPtr(strLstJoin(perlCommand(), "|")),
|
||||
TEST_ENV_EXE "|" TEST_PERL_EXE "|-I.|-MDevel::Cover=-silent,1|" TEST_PERL_MAIN "'"
|
||||
", '--db-include', 'db1', '--db-include', 'db2'"
|
||||
", '--perl-option', '-I.', '--perl-option', '-MDevel::Cover=-silent,1'"
|
||||
", '--recovery-option', 'standby_mode=on', '--recovery-option', 'primary_conn_info=blah'"
|
||||
", 'help', 'restore', 'param1', 'param2')|[NULL]", "complex options");
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------------------------------------------------------
|
||||
|
Reference in New Issue
Block a user