1
0
mirror of https://github.com/pgbackrest/pgbackrest.git synced 2024-12-12 10:04:14 +02:00

Fix non-compliant JSON for options passed from C to Perl.

We have been using a hacked-up JSON generator to pass options from C to Perl since the C binary was introduced.  This generator was not very compliant which led to issues with \n, ", etc. inside strings.

We have a fully-compliant JSON generator now so use that instead.

Reported by Leo Khomenko.
This commit is contained in:
David Steele 2019-02-22 12:02:26 +02:00
parent 70c30dfb61
commit 1f66bda02e
7 changed files with 69 additions and 67 deletions

View File

@ -30,6 +30,14 @@
<p>Fix info command missing WAL min/max when stanza specified.</p>
</release-item>
<release-item>
<release-item-contributor-list>
<release-item-ideator id="leo.khomenko"/>
</release-item-contributor-list>
<p>Fix non-compliant JSON for options passed from C to Perl.</p>
</release-item>
</release-bug-list>
<release-development-list>
@ -6469,6 +6477,11 @@
<contributor-id type="github">LaetitiaLoxo</contributor-id>
</contributor>
<contributor id="leo.khomenko">
<contributor-name-display>Leo Khomenko</contributor-name-display>
<contributor-id type="github">lkhomenk</contributor-id>
</contributor>
<contributor id="leonardo.gg.avellar">
<contributor-name-display>Leonardo GG Avellar</contributor-name-display>
<contributor-id type="github">L30Bola</contributor-id>

View File

@ -93,9 +93,6 @@ sub configLoad
eval
{
# Hacky fix for backslashes that need to be escaped
$$rstrConfigJson =~ s/\\/\\\\/g;
%oOption = %{(JSON::PP->new()->allow_nonref())->decode($$rstrConfigJson)};
return true;
}

View File

@ -66,6 +66,7 @@ my @stryCFile =
'common/time.c',
'common/type/convert.c',
'common/type/buffer.c',
'common/type/json.c',
'common/type/keyValue.c',
'common/type/list.c',
'common/type/string.c',

View File

@ -380,7 +380,7 @@ info/infoPg.o: info/infoPg.c common/assert.h common/debug.h common/error.auto.h
main.o: main.c command/archive/get/get.h command/archive/push/push.h command/command.h command/help/help.h command/info/info.h command/remote/remote.h common/assert.h common/debug.h common/error.auto.h common/error.h common/exit.h common/lock.h common/log.h common/logLevel.h common/memContext.h common/stackTrace.h common/time.h common/type/buffer.h common/type/convert.h common/type/keyValue.h common/type/string.h common/type/stringList.h common/type/variant.h common/type/variantList.h config/config.auto.h config/config.h config/define.auto.h config/define.h config/load.h perl/exec.h postgres/interface.h version.h
$(CC) $(CFLAGS) -c main.c -o main.o
perl/config.o: perl/config.c common/assert.h common/debug.h common/error.auto.h common/error.h common/lock.h common/log.h common/logLevel.h common/memContext.h common/stackTrace.h common/time.h common/type/buffer.h common/type/convert.h common/type/keyValue.h common/type/string.h common/type/stringList.h common/type/variant.h common/type/variantList.h config/config.auto.h config/config.h config/define.auto.h config/define.h
perl/config.o: perl/config.c common/assert.h common/debug.h common/error.auto.h common/error.h common/lock.h common/log.h common/logLevel.h common/memContext.h common/stackTrace.h common/time.h common/type/buffer.h common/type/convert.h common/type/json.h common/type/keyValue.h common/type/string.h common/type/stringList.h common/type/variant.h common/type/variantList.h config/config.auto.h config/config.h config/define.auto.h config/define.h
$(CC) $(CFLAGS) -c perl/config.c -o perl/config.o
perl/exec.o: perl/exec.c ../libc/LibC.h common/assert.h common/debug.h common/encode.h common/error.auto.h common/error.h common/io/filter/filter.h common/io/filter/group.h common/io/read.h common/io/write.h common/lock.h common/log.h common/logLevel.h common/memContext.h common/stackTrace.h common/time.h common/type/buffer.h common/type/convert.h common/type/keyValue.h common/type/string.h common/type/stringList.h common/type/variant.h common/type/variantList.h config/config.auto.h config/config.h config/define.auto.h config/define.h config/load.h config/parse.h crypto/cipherBlock.h crypto/crypto.h crypto/hash.h perl/config.h perl/embed.auto.c perl/exec.h perl/libc.auto.c postgres/pageChecksum.h storage/driver/posix/fileRead.h storage/driver/posix/fileWrite.h storage/driver/posix/storage.h storage/fileRead.h storage/fileWrite.h storage/info.h storage/storage.h storage/storage.intern.h version.h ../libc/xs/common/encode.xsh ../libc/xs/crypto/cipherBlock.xsh ../libc/xs/crypto/hash.xsh

View File

@ -3,6 +3,7 @@ Perl Configuration
***********************************************************************************************************************************/
#include "common/debug.h"
#include "common/memContext.h"
#include "common/type/json.h"
#include "config/config.h"
/***********************************************************************************************************************************
@ -13,56 +14,57 @@ perlOptionJson(void)
{
FUNCTION_TEST_VOID();
String *result = strNew("{");
String *result = NULL;
MEM_CONTEXT_TEMP_BEGIN()
{
KeyValue *configKv = kvNew();
for (ConfigOption optionId = 0; optionId < CFG_OPTION_TOTAL; optionId++)
{
// Skip if not valid
if (!cfgOptionValid(optionId))
continue;
// Add comma if not first valid option
if (strSize(result) != 1)
strCat(result, ",");
Variant *optionVar = varNewKv();
// Add valid and source
strCatFmt(result, "\"%s\":{\"valid\":true,\"source\":\"", cfgOptionName(optionId));
// Add valid
kvPut(varKv(optionVar), varNewStr(strNew("valid")), varNewBool(true));
// Add source
const char *source = NULL;
switch (cfgOptionSource(optionId))
{
case cfgSourceParam:
{
strCat(result, "param");
source = "param";
break;
}
case cfgSourceConfig:
{
strCat(result, "config");
source = "config";
break;
}
case cfgSourceDefault:
{
strCat(result, "default");
source = "default";
break;
}
}
strCat(result, "\"");
kvPut(varKv(optionVar), varNewStr(strNew("source")), varNewStr(strNew(source)));
// Add negate
strCatFmt(result, ",\"negate\":%s", strPtr(varStrForce(varNewBool(cfgOptionNegate(optionId)))));
// Add reset
strCatFmt(result, ",\"reset\":%s", strPtr(varStrForce(varNewBool(cfgOptionReset(optionId)))));
// Add negate and reset
kvPut(varKv(optionVar), varNewStr(strNew("negate")), varNewBool(cfgOptionNegate(optionId)));
kvPut(varKv(optionVar), varNewStr(strNew("reset")), varNewBool(cfgOptionReset(optionId)));
// Add value if it is set
if (cfgOptionTest(optionId))
{
strCat(result, ",\"value\":");
const Variant *valueVar = NULL;
switch (cfgDefOptionType(cfgOptionDefIdFromId(optionId)))
{
@ -70,64 +72,47 @@ perlOptionJson(void)
case cfgDefOptTypeFloat:
case cfgDefOptTypeInteger:
case cfgDefOptTypeSize:
{
strCat(result, strPtr(varStrForce(cfgOption(optionId))));
break;
}
case cfgDefOptTypeString:
{
strCatFmt(result, "\"%s\"", strPtr(cfgOptionStr(optionId)));
valueVar = cfgOption(optionId);
break;
}
case cfgDefOptTypeHash:
{
valueVar = varNewKv();
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, "}");
kvPut(varKv(valueVar), varLstGet(keyList, listIdx), kvGet(valueKv, varLstGet(keyList, listIdx)));
break;
}
case cfgDefOptTypeList:
{
StringList *valueList = strLstNewVarLst(cfgOptionLst(optionId));
valueVar = varNewKv();
strCat(result, "{");
const VariantList *valueList = cfgOptionLst(optionId);
for (unsigned int listIdx = 0; listIdx < strLstSize(valueList); listIdx++)
{
if (listIdx != 0)
strCat(result, ",");
strCatFmt(result, "\"%s\":true", strPtr(strLstGet(valueList, listIdx)));
}
strCat(result, "}");
for (unsigned int listIdx = 0; listIdx < varLstSize(valueList); listIdx++)
kvPut(varKv(valueVar), varLstGet(valueList, listIdx), varNewBool(true));
break;
}
}
kvPut(varKv(optionVar), varNewStr(strNew("value")), valueVar);
}
strCat(result, "}");
kvPut(configKv, varNewStr(strNew(cfgOptionName(optionId))), optionVar);
}
strCat(result, "}");
memContextSwitch(MEM_CONTEXT_OLD());
result = kvToJson(configKv, 0);
memContextSwitch(MEM_CONTEXT_TEMP());
}
MEM_CONTEXT_TEMP_END();

View File

@ -8534,9 +8534,6 @@ static const EmbeddedModule embeddedModule[] =
"\n"
"eval\n"
"{\n"
"\n"
"$$rstrConfigJson =~ s/\\\\/\\\\\\\\/g;\n"
"\n"
"%oOption = %{(JSON::PP->new()->allow_nonref())->decode($$rstrConfigJson)};\n"
"return true;\n"
"}\n"

View File

@ -34,6 +34,9 @@ testRun(void)
cfgOptionValidSet(cfgOptPgHost, true);
cfgOptionResetSet(cfgOptPgHost, true);
cfgOptionValidSet(cfgOptPgPath, true);
cfgOptionSet(cfgOptPgPath, cfgSourceConfig, varNewStr(strNew("/path/db/pg")));
cfgOptionValidSet(cfgOptBackupStandby, true);
cfgOptionResetSet(cfgOptBackupStandby, true);
cfgOptionSet(cfgOptBackupStandby, cfgSourceParam, varNewBool(false));
@ -44,6 +47,9 @@ testRun(void)
cfgOptionValidSet(cfgOptArchivePushQueueMax, true);
cfgOptionSet(cfgOptArchivePushQueueMax, cfgSourceParam, varNewInt64(999999999999));
cfgOptionValidSet(cfgOptRepoCipherPass, true);
cfgOptionSet(cfgOptRepoCipherPass, cfgSourceConfig, varNewStr(strNew("part1\npart2")));
cfgOptionValidSet(cfgOptCompressLevel, true);
cfgOptionSet(cfgOptCompressLevel, cfgSourceConfig, varNewInt(3));
@ -54,15 +60,18 @@ testRun(void)
strPtr(perlOptionJson()),
"{"
"\"archive-push-queue-max\":"
"{\"valid\":true,\"source\":\"param\",\"negate\":false,\"reset\":false,\"value\":999999999999},"
"\"backup-standby\":{\"valid\":true,\"source\":\"param\",\"negate\":false,\"reset\":true,\"value\":false},"
"\"compress\":{\"valid\":true,\"source\":\"param\",\"negate\":false,\"reset\":false,\"value\":true},"
"\"compress-level\":{\"valid\":true,\"source\":\"config\",\"negate\":false,\"reset\":false,\"value\":3},"
"\"config\":{\"valid\":true,\"source\":\"param\",\"negate\":true,\"reset\":false},"
"\"online\":{\"valid\":true,\"source\":\"param\",\"negate\":true,\"reset\":false,\"value\":false},"
"\"pg1-host\":{\"valid\":true,\"source\":\"default\",\"negate\":false,\"reset\":true},"
"\"protocol-timeout\":{\"valid\":true,\"source\":\"param\",\"negate\":false,\"reset\":false,\"value\":1.1},"
"\"stanza\":{\"valid\":true,\"source\":\"default\",\"negate\":false,\"reset\":false,\"value\":\"db\"}"
"{\"negate\":false,\"reset\":false,\"source\":\"param\",\"valid\":true,\"value\":999999999999},"
"\"backup-standby\":{\"negate\":false,\"reset\":true,\"source\":\"param\",\"valid\":true,\"value\":false},"
"\"compress\":{\"negate\":false,\"reset\":false,\"source\":\"param\",\"valid\":true,\"value\":true},"
"\"compress-level\":{\"negate\":false,\"reset\":false,\"source\":\"config\",\"valid\":true,\"value\":3},"
"\"config\":{\"negate\":true,\"reset\":false,\"source\":\"param\",\"valid\":true},"
"\"online\":{\"negate\":true,\"reset\":false,\"source\":\"param\",\"valid\":true,\"value\":false},"
"\"pg1-host\":{\"negate\":false,\"reset\":true,\"source\":\"default\",\"valid\":true},"
"\"pg1-path\":{\"negate\":false,\"reset\":false,\"source\":\"config\",\"valid\":true,\"value\":\"\\/path\\/db\\/pg\"},"
"\"protocol-timeout\":{\"negate\":false,\"reset\":false,\"source\":\"param\",\"valid\":true,\"value\":1.1},"
"\"repo1-cipher-pass\":{\"negate\":false,\"reset\":false,\"source\":\"config\",\"valid\":true,"
"\"value\":\"part1\\npart2\"},"
"\"stanza\":{\"negate\":false,\"reset\":false,\"source\":\"default\",\"valid\":true,\"value\":\"db\"}"
"}",
"simple options");
@ -97,12 +106,12 @@ testRun(void)
TEST_RESULT_STR(
strPtr(perlOptionJson()),
"{"
"\"db-include\":{\"valid\":true,\"source\":\"param\",\"negate\":false,\"reset\":false,"
"\"db-include\":{\"negate\":false,\"reset\":false,\"source\":\"param\",\"valid\":true,"
"\"value\":{\"db1\":true,\"db2\":true}},"
"\"perl-option\":{\"valid\":true,\"source\":\"param\",\"negate\":false,\"reset\":false,"
"\"perl-option\":{\"negate\":false,\"reset\":false,\"source\":\"param\",\"valid\":true,"
"\"value\":{\"-I.\":true,\"-MDevel::Cover=-silent,1\":true}},"
"\"recovery-option\":{\"valid\":true,\"source\":\"param\",\"negate\":false,\"reset\":false,"
"\"value\":{\"standby_mode\":\"on\",\"primary_conn_info\":\"blah\"}}"
"\"recovery-option\":{\"negate\":false,\"reset\":false,\"source\":\"param\",\"valid\":true,"
"\"value\":{\"primary_conn_info\":\"blah\",\"standby_mode\":\"on\"}}"
"}",
"complex options");
}