diff --git a/libc/LibC.xs b/libc/LibC.xs index 9ab6eebdb..12f426370 100644 --- a/libc/LibC.xs +++ b/libc/LibC.xs @@ -46,7 +46,7 @@ These includes are from the src directory. There is no Perl-specific code in th #include "config/config.h" #include "config/define.h" #include "config/load.h" -#include "perl/exec.h" +#include "perl/config.h" #include "postgres/pageChecksum.h" /*********************************************************************************************************************************** diff --git a/libc/Makefile.PL b/libc/Makefile.PL index 276f69a48..0bb4bb0b5 100644 --- a/libc/Makefile.PL +++ b/libc/Makefile.PL @@ -96,7 +96,7 @@ my @stryCFile = 'config/define.c', 'config/load.c', 'config/parse.c', - 'perl/exec.c', + 'perl/config.c', 'postgres/pageChecksum.c', 'storage/helper.c', 'storage/storage.c', diff --git a/src/Makefile b/src/Makefile index 5104ee08c..3d8fd324b 100644 --- a/src/Makefile +++ b/src/Makefile @@ -25,6 +25,7 @@ pgbackrest: \ config/define.o \ config/load.o \ config/parse.o \ + perl/config.o \ perl/exec.o \ storage/helper.o \ storage/storage.o \ @@ -52,6 +53,7 @@ pgbackrest: \ config/define.o \ config/load.o \ config/parse.o \ + perl/config.o \ perl/exec.o \ storage/helper.o \ storage/storage.o \ diff --git a/src/perl/config.c b/src/perl/config.c new file mode 100644 index 000000000..97a73a071 --- /dev/null +++ b/src/perl/config.c @@ -0,0 +1,132 @@ +/*********************************************************************************************************************************** +Perl Configuration +***********************************************************************************************************************************/ +#include "common/memContext.h" +#include "config/config.h" + +/*********************************************************************************************************************************** +Build JSON output from options +***********************************************************************************************************************************/ +String * +perlOptionJson() +{ + String *result = strNew("{"); + + MEM_CONTEXT_TEMP_BEGIN() + { + for (ConfigOption optionId = 0; optionId < CFG_OPTION_TOTAL; optionId++) + { + // Skip the option if it is not valid + if (!cfgOptionValid(optionId)) + continue; + + // Output option + if (strSize(result) != 1) + strCat(result, ","); + + strCatFmt(result, "\"%s\":{", cfgOptionName(optionId)); + + // Output source unless it is default + if (cfgOptionSource(optionId) != cfgSourceDefault) + { + strCat(result, "\"source\":\""); + + if (cfgOptionSource(optionId) == cfgSourceParam) + strCat(result, "param"); + else + strCat(result, "config"); + + strCat(result, "\""); + + // Add a comma if another define will be added + if (cfgOption(optionId) != NULL) + strCat(result, ","); + } + + // If option was negated + if (cfgOptionNegate(optionId)) + strCatFmt(result, "\"negate\":%s", strPtr(varStrForce(varNewBool(true)))); + else + { + // If option is reset then add flag + if (cfgOptionReset(optionId)) + strCatFmt(result, "\"reset\":%s", strPtr(varStrForce(varNewBool(true)))); + + // If has a value + if (cfgOption(optionId) != NULL) + { + // If option is reset, then add a comma separator before setting the value + if (cfgOptionReset(optionId)) + strCat(result, ","); + + strCat(result, "\"value\":"); + + switch (cfgDefOptionType(cfgOptionDefIdFromId(optionId))) + { + case cfgDefOptTypeBoolean: + case cfgDefOptTypeFloat: + case cfgDefOptTypeInteger: + { + strCat(result, strPtr(varStrForce(cfgOption(optionId)))); + break; + } + + case cfgDefOptTypeString: + { + strCatFmt(result, "\"%s\"", strPtr(cfgOptionStr(optionId))); + break; + } + + case cfgDefOptTypeHash: + { + const KeyValue *valueKv = cfgOptionKv(optionId); + const VariantList *keyList = kvKeyList(valueKv); + + strCat(result, "{"); + + for (unsigned int listIdx = 0; listIdx < varLstSize(keyList); listIdx++) + { + if (listIdx != 0) + strCat(result, ","); + + strCatFmt( + result, "\"%s\":\"%s\"", strPtr(varStr(varLstGet(keyList, listIdx))), + strPtr(varStr(kvGet(valueKv, varLstGet(keyList, listIdx))))); + } + + strCat(result, "}"); + + break; + } + + case cfgDefOptTypeList: + { + StringList *valueList = strLstNewVarLst(cfgOptionLst(optionId)); + + strCat(result, "{"); + + for (unsigned int listIdx = 0; listIdx < strLstSize(valueList); listIdx++) + { + if (listIdx != 0) + strCat(result, ","); + + strCatFmt(result, "\"%s\":true", strPtr(strLstGet(valueList, listIdx))); + } + + strCat(result, "}"); + + break; + } + } + } + } + + strCat(result, "}"); + } + + strCat(result, "}"); + } + MEM_CONTEXT_TEMP_END(); + + return result; +} diff --git a/src/perl/config.h b/src/perl/config.h new file mode 100644 index 000000000..71d1d7080 --- /dev/null +++ b/src/perl/config.h @@ -0,0 +1,14 @@ +/*********************************************************************************************************************************** +Perl Configuration +***********************************************************************************************************************************/ +#ifndef PERL_CONFIG_H +#define PERL_CONFIG_H + +#include "common/type.h" + +/*********************************************************************************************************************************** +Functions +***********************************************************************************************************************************/ +String *perlOptionJson(); + +#endif diff --git a/src/perl/exec.c b/src/perl/exec.c index e4511adf1..abdae91af 100644 --- a/src/perl/exec.c +++ b/src/perl/exec.c @@ -12,6 +12,7 @@ Execute Perl for Legacy Functionality #include "common/memContext.h" #include "common/type.h" #include "config/config.h" +#include "perl/config.h" /*********************************************************************************************************************************** Constants used to build perl options @@ -24,133 +25,6 @@ Constants used to build perl options #define PGBACKREST_MODULE PGBACKREST_NAME "::Main" #define PGBACKREST_MAIN PGBACKREST_MODULE "::main" -/*********************************************************************************************************************************** -Build JSON output from options -***********************************************************************************************************************************/ -String * -perlOptionJson() -{ - String *result = strNew("{"); - - MEM_CONTEXT_TEMP_BEGIN() - { - for (ConfigOption optionId = 0; optionId < CFG_OPTION_TOTAL; optionId++) - { - // Skip the option if it is not valid - if (!cfgOptionValid(optionId)) - continue; - - // Output option - if (strSize(result) != 1) - strCat(result, ","); - - strCatFmt(result, "\"%s\":{", cfgOptionName(optionId)); - - // Output source unless it is default - if (cfgOptionSource(optionId) != cfgSourceDefault) - { - strCat(result, "\"source\":\""); - - if (cfgOptionSource(optionId) == cfgSourceParam) - strCat(result, "param"); - else - strCat(result, "config"); - - strCat(result, "\""); - - // Add a comma if another define will be added - if (cfgOption(optionId) != NULL) - strCat(result, ","); - } - - // If option was negated - if (cfgOptionNegate(optionId)) - strCatFmt(result, "\"negate\":%s", strPtr(varStrForce(varNewBool(true)))); - else - { - // If option is reset then add indicator - if (cfgOptionReset(optionId)) - strCatFmt(result, "\"reset\":%s", strPtr(varStrForce(varNewBool(true)))); - - // Else not negated and has a value - if (cfgOption(optionId) != NULL) - { - // If option is reset, then add a comma separator before setting the value - if (cfgOptionReset(optionId)) - strCat(result, ","); - - strCat(result, "\"value\":"); - - switch (cfgDefOptionType(cfgOptionDefIdFromId(optionId))) - { - case cfgDefOptTypeBoolean: - case cfgDefOptTypeFloat: - case cfgDefOptTypeInteger: - { - strCat(result, strPtr(varStrForce(cfgOption(optionId)))); - break; - } - - case cfgDefOptTypeString: - { - strCatFmt(result, "\"%s\"", strPtr(cfgOptionStr(optionId))); - break; - } - - case cfgDefOptTypeHash: - { - const KeyValue *valueKv = cfgOptionKv(optionId); - const VariantList *keyList = kvKeyList(valueKv); - - strCat(result, "{"); - - for (unsigned int listIdx = 0; listIdx < varLstSize(keyList); listIdx++) - { - if (listIdx != 0) - strCat(result, ","); - - strCatFmt( - result, "\"%s\":\"%s\"", strPtr(varStr(varLstGet(keyList, listIdx))), - strPtr(varStr(kvGet(valueKv, varLstGet(keyList, listIdx))))); - } - - strCat(result, "}"); - - break; - } - - case cfgDefOptTypeList: - { - StringList *valueList = strLstNewVarLst(cfgOptionLst(optionId)); - - strCat(result, "{"); - - for (unsigned int listIdx = 0; listIdx < strLstSize(valueList); listIdx++) - { - if (listIdx != 0) - strCat(result, ","); - - strCatFmt(result, "\"%s\":true", strPtr(strLstGet(valueList, listIdx))); - } - - strCat(result, "}"); - - break; - } - } - } - } - - strCat(result, "}"); - } - - strCat(result, "}"); - } - MEM_CONTEXT_TEMP_END(); - - return result; -} - /*********************************************************************************************************************************** Build list of perl options to use for exec ***********************************************************************************************************************************/ diff --git a/test/lib/pgBackRestTest/Common/DefineTest.pm b/test/lib/pgBackRestTest/Common/DefineTest.pm index f417fb140..46847ea05 100644 --- a/test/lib/pgBackRestTest/Common/DefineTest.pm +++ b/test/lib/pgBackRestTest/Common/DefineTest.pm @@ -388,6 +388,17 @@ my $oTestDef = &TESTDEF_TEST => [ + { + &TESTDEF_NAME => 'config', + &TESTDEF_TOTAL => 1, + &TESTDEF_C => true, + + &TESTDEF_COVERAGE => + { + 'perl/config' => TESTDEF_COVERAGE_FULL, + }, + }, + { &TESTDEF_NAME => 'exec', &TESTDEF_TOTAL => 2, diff --git a/test/src/module/perl/configTest.c b/test/src/module/perl/configTest.c new file mode 100644 index 000000000..4bc1d3b98 --- /dev/null +++ b/test/src/module/perl/configTest.c @@ -0,0 +1,98 @@ +/*********************************************************************************************************************************** +Test Perl Exec +***********************************************************************************************************************************/ +#include "config/config.h" + +/*********************************************************************************************************************************** +Test Run +***********************************************************************************************************************************/ +void +testRun() +{ + // ----------------------------------------------------------------------------------------------------------------------------- + if (testBegin("perlMain()")) + { + cfgInit(); + TEST_RESULT_STR(strPtr(perlOptionJson()), "{}", "no options"); + + // ------------------------------------------------------------------------------------------------------------------------- + cfgInit(); + + cfgOptionValidSet(cfgOptCompress, true); + cfgOptionSet(cfgOptCompress, cfgSourceParam, varNewBool(true)); + + cfgOptionValidSet(cfgOptOnline, true); + cfgOptionNegateSet(cfgOptOnline, true); + cfgOptionSet(cfgOptOnline, cfgSourceParam, varNewBool(false)); + + cfgOptionValidSet(cfgOptPgHost, true); + cfgOptionResetSet(cfgOptPgHost, true); + + cfgOptionValidSet(cfgOptBackupStandby, true); + cfgOptionResetSet(cfgOptBackupStandby, true); + cfgOptionSet(cfgOptBackupStandby, cfgSourceDefault, varNewBool(false)); + + cfgOptionValidSet(cfgOptProtocolTimeout, true); + cfgOptionSet(cfgOptProtocolTimeout, cfgSourceParam, varNewDbl(1.1)); + + cfgOptionValidSet(cfgOptArchiveQueueMax, true); + cfgOptionSet(cfgOptArchiveQueueMax, cfgSourceParam, varNewInt64(999999999999)); + + cfgOptionValidSet(cfgOptCompressLevel, true); + cfgOptionSet(cfgOptCompressLevel, cfgSourceConfig, varNewInt(3)); + + cfgOptionValidSet(cfgOptStanza, true); + cfgOptionSet(cfgOptStanza, cfgSourceDefault, varNewStr(strNew("db"))); + + TEST_RESULT_STR( + strPtr(perlOptionJson()), + "{" + "\"archive-queue-max\":{\"source\":\"param\",\"value\":999999999999}," + "\"backup-standby\":{\"reset\":true,\"value\":false}," + "\"compress\":{\"source\":\"param\",\"value\":true}," + "\"compress-level\":{\"source\":\"config\",\"value\":3}," + "\"online\":{\"source\":\"param\",\"negate\":true}," + "\"pg1-host\":{\"reset\":true}," + "\"protocol-timeout\":{\"source\":\"param\",\"value\":1.1}," + "\"stanza\":{\"value\":\"db\"}" + "}", + "simple options"); + + // ------------------------------------------------------------------------------------------------------------------------- + cfgInit(); + + 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); + StringList *perlList = strLstNew(); + strLstAdd(perlList, strNew("-I.")); + strLstAdd(perlList, strNew("-MDevel::Cover=-silent,1")); + cfgOptionSet(cfgOptPerlOption, cfgSourceParam, varNewVarLst(varLstNewStrLst(perlList))); + + TEST_RESULT_STR( + strPtr(perlOptionJson()), + "{" + "\"db-include\":{\"source\":\"param\",\"value\":{\"db1\":true,\"db2\":true}}," + "\"perl-option\":{\"source\":\"param\",\"value\":{\"-I.\":true,\"-MDevel::Cover=-silent,1\":true}}," + "\"recovery-option\":{\"source\":\"param\",\"value\":{\"standby_mode\":\"on\",\"primary_conn_info\":\"blah\"}}" + "}", + "complex options"); + } +} diff --git a/test/src/module/perl/execTest.c b/test/src/module/perl/execTest.c index 78a9c8652..7d2c8c964 100644 --- a/test/src/module/perl/execTest.c +++ b/test/src/module/perl/execTest.c @@ -2,12 +2,7 @@ Test Perl Exec ***********************************************************************************************************************************/ #include "config/config.h" - -#define TEST_ENV_EXE "/usr/bin/env" -#define TEST_PERL_EXE "perl" -#define TEST_BACKREST_EXE "/path/to/pgbackrest" -#define TEST_PERL_MAIN \ - "-MpgBackRest::Main|-e|pgBackRest::Main::main('" TEST_BACKREST_EXE "" +#include "config/load.h" /*********************************************************************************************************************************** Test Run @@ -16,92 +11,27 @@ void testRun() { // ----------------------------------------------------------------------------------------------------------------------------- - if (testBegin("perlCommand()")) + if (testBegin("perlMain()")) { - // ------------------------------------------------------------------------------------------------------------------------- cfgInit(); cfgCommandSet(cfgCmdInfo); - cfgExeSet(strNew(TEST_BACKREST_EXE)); + cfgExeSet(strNew("/path/to/pgbackrest")); cfgOptionSet(cfgOptPerlBin, cfgSourceParam, varNewStrZ("/usr/bin/perl")); TEST_RESULT_STR( strPtr(strLstJoin(perlCommand(), "|")), - "/usr/bin/perl|" TEST_PERL_MAIN "','info','{}')|[NULL]", "custom command with no options"); - - cfgOptionSet(cfgOptPerlBin, cfgSourceParam, NULL); - - TEST_RESULT_STR( - strPtr(strLstJoin(perlCommand(), "|")), - TEST_ENV_EXE "|" TEST_PERL_EXE "|" TEST_PERL_MAIN "','info','{}')|[NULL]", "command with no options"); + "/usr/bin/perl|-MpgBackRest::Main|-e|pgBackRest::Main::main('/path/to/pgbackrest','info','{}')|[NULL]", + "custom command with no options"); // ------------------------------------------------------------------------------------------------------------------------- - cfgInit(); - cfgCommandSet(cfgCmdBackup); - cfgExeSet(strNew(TEST_BACKREST_EXE)); + cfgOptionSet(cfgOptPerlBin, cfgSourceParam, NULL); cfgOptionValidSet(cfgOptCompress, true); cfgOptionSet(cfgOptCompress, cfgSourceParam, varNewBool(true)); - cfgOptionValidSet(cfgOptOnline, true); - cfgOptionNegateSet(cfgOptOnline, true); - cfgOptionSet(cfgOptOnline, cfgSourceParam, varNewBool(false)); - - cfgOptionValidSet(cfgOptPgHost, true); - cfgOptionResetSet(cfgOptPgHost, true); - - cfgOptionValidSet(cfgOptBackupStandby, true); - cfgOptionResetSet(cfgOptBackupStandby, true); - cfgOptionSet(cfgOptBackupStandby, cfgSourceDefault, varNewBool(false)); - - cfgOptionValidSet(cfgOptProtocolTimeout, true); - cfgOptionSet(cfgOptProtocolTimeout, cfgSourceParam, varNewDbl(1.1)); - - cfgOptionValidSet(cfgOptArchiveQueueMax, true); - cfgOptionSet(cfgOptArchiveQueueMax, cfgSourceParam, varNewInt64(999999999999)); - - cfgOptionValidSet(cfgOptCompressLevel, true); - cfgOptionSet(cfgOptCompressLevel, cfgSourceConfig, varNewInt(3)); - - cfgOptionValidSet(cfgOptStanza, true); - cfgOptionSet(cfgOptStanza, cfgSourceDefault, varNewStr(strNew("db"))); - - TEST_RESULT_STR( - strPtr(strLstJoin(perlCommand(), "|")), - TEST_ENV_EXE "|" TEST_PERL_EXE "|" TEST_PERL_MAIN "','backup','{" - "\"archive-queue-max\":{\"source\":\"param\",\"value\":999999999999}," - "\"backup-standby\":{\"reset\":true,\"value\":false}," - "\"compress\":{\"source\":\"param\",\"value\":true}," - "\"compress-level\":{\"source\":\"config\",\"value\":3}," - "\"online\":{\"source\":\"param\",\"negate\":true}," - "\"pg1-host\":{\"reset\":true}," - "\"protocol-timeout\":{\"source\":\"param\",\"value\":1.1}," - "\"stanza\":{\"value\":\"db\"}" - "}')|[NULL]", - "simple options"); - - // ------------------------------------------------------------------------------------------------------------------------- - 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")); + strLstAdd(commandParamList, strNew("A")); + strLstAdd(commandParamList, strNew("B")); cfgCommandParamSet(commandParamList); cfgOptionValidSet(cfgOptPerlOption, true); @@ -112,11 +42,12 @@ testRun() TEST_RESULT_STR( strPtr(strLstJoin(perlCommand(), "|")), - TEST_ENV_EXE "|" TEST_PERL_EXE "|-I.|-MDevel::Cover=-silent,1|" TEST_PERL_MAIN "','restore','{" - "\"db-include\":{\"source\":\"param\",\"value\":{\"db1\":true,\"db2\":true}}," - "\"perl-option\":{\"source\":\"param\",\"value\":{\"-I.\":true,\"-MDevel::Cover=-silent,1\":true}}," - "\"recovery-option\":{\"source\":\"param\",\"value\":{\"standby_mode\":\"on\",\"primary_conn_info\":\"blah\"}}" - "}','param1','param2')|[NULL]", "complex options"); + "/usr/bin/env|perl|-I.|-MDevel::Cover=-silent,1|-MpgBackRest::Main|-e|pgBackRest::Main::main(" + "'/path/to/pgbackrest','info','{" + "\"compress\":{\"source\":\"param\",\"value\":true}," + "\"perl-option\":{\"source\":\"param\",\"value\":{\"-I.\":true,\"-MDevel::Cover=-silent,1\":true}}" + "}','A','B')|[NULL]", + "command with one option and params"); } // -----------------------------------------------------------------------------------------------------------------------------