2019-01-18 21:32:51 +02:00
|
|
|
/***********************************************************************************************************************************
|
|
|
|
Protocol Helper
|
|
|
|
***********************************************************************************************************************************/
|
2019-04-26 08:08:23 -04:00
|
|
|
#include "build.auto.h"
|
|
|
|
|
2019-04-17 08:04:22 -04:00
|
|
|
#include <string.h>
|
|
|
|
|
2019-03-10 13:27:30 +02:00
|
|
|
#include "common/crypto/common.h"
|
2019-01-18 21:32:51 +02:00
|
|
|
#include "common/debug.h"
|
|
|
|
#include "common/exec.h"
|
|
|
|
#include "common/memContext.h"
|
|
|
|
#include "config/config.h"
|
|
|
|
#include "config/exec.h"
|
2019-02-19 20:57:38 +02:00
|
|
|
#include "config/protocol.h"
|
2020-02-12 15:47:07 -07:00
|
|
|
#include "postgres/version.h"
|
2019-01-18 21:32:51 +02:00
|
|
|
#include "protocol/helper.h"
|
2020-02-12 15:47:07 -07:00
|
|
|
#include "version.h"
|
2019-01-18 21:32:51 +02:00
|
|
|
|
|
|
|
/***********************************************************************************************************************************
|
|
|
|
Constants
|
|
|
|
***********************************************************************************************************************************/
|
2019-02-27 22:34:21 +02:00
|
|
|
STRING_EXTERN(PROTOCOL_SERVICE_LOCAL_STR, PROTOCOL_SERVICE_LOCAL);
|
2019-01-18 21:32:51 +02:00
|
|
|
STRING_EXTERN(PROTOCOL_SERVICE_REMOTE_STR, PROTOCOL_SERVICE_REMOTE);
|
|
|
|
|
2020-01-08 18:59:02 -07:00
|
|
|
STRING_STATIC(PROTOCOL_REMOTE_TYPE_PG_STR, PROTOCOL_REMOTE_TYPE_PG);
|
|
|
|
STRING_STATIC(PROTOCOL_REMOTE_TYPE_REPO_STR, PROTOCOL_REMOTE_TYPE_REPO);
|
2019-11-23 10:22:11 -05:00
|
|
|
|
2019-01-18 21:32:51 +02:00
|
|
|
/***********************************************************************************************************************************
|
|
|
|
Local variables
|
|
|
|
***********************************************************************************************************************************/
|
2019-02-27 22:34:21 +02:00
|
|
|
typedef struct ProtocolHelperClient
|
|
|
|
{
|
|
|
|
Exec *exec; // Executed client
|
|
|
|
ProtocolClient *client; // Protocol client
|
|
|
|
} ProtocolHelperClient;
|
|
|
|
|
2019-01-18 21:32:51 +02:00
|
|
|
static struct
|
|
|
|
{
|
|
|
|
MemContext *memContext; // Mem context for protocol helper
|
|
|
|
|
2019-08-21 14:14:30 -04:00
|
|
|
unsigned int clientRemoteSize; // Remote clients
|
2019-02-27 22:34:21 +02:00
|
|
|
ProtocolHelperClient *clientRemote;
|
|
|
|
|
2019-08-21 14:14:30 -04:00
|
|
|
unsigned int clientLocalSize; // Local clients
|
2019-02-27 22:34:21 +02:00
|
|
|
ProtocolHelperClient *clientLocal;
|
2019-01-18 21:32:51 +02:00
|
|
|
} protocolHelper;
|
|
|
|
|
2019-02-27 22:34:21 +02:00
|
|
|
/***********************************************************************************************************************************
|
|
|
|
Init local mem context and data structure
|
|
|
|
***********************************************************************************************************************************/
|
|
|
|
static void
|
|
|
|
protocolHelperInit(void)
|
|
|
|
{
|
|
|
|
// In the protocol helper has not been initialized
|
|
|
|
if (protocolHelper.memContext == NULL)
|
|
|
|
{
|
|
|
|
// Create a mem context to store protocol objects
|
|
|
|
MEM_CONTEXT_BEGIN(memContextTop())
|
|
|
|
{
|
2020-01-17 11:58:41 -07:00
|
|
|
MEM_CONTEXT_NEW_BEGIN("ProtocolHelper")
|
|
|
|
{
|
|
|
|
protocolHelper.memContext = MEM_CONTEXT_NEW();
|
|
|
|
}
|
|
|
|
MEM_CONTEXT_NEW_END();
|
2019-02-27 22:34:21 +02:00
|
|
|
}
|
|
|
|
MEM_CONTEXT_END();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-03 18:01:28 -04:00
|
|
|
/**********************************************************************************************************************************/
|
2019-01-18 21:32:51 +02:00
|
|
|
bool
|
|
|
|
repoIsLocal(void)
|
|
|
|
{
|
|
|
|
FUNCTION_TEST_VOID();
|
2019-01-28 15:06:28 +02:00
|
|
|
FUNCTION_TEST_RETURN(!cfgOptionTest(cfgOptRepoHost));
|
2019-01-18 21:32:51 +02:00
|
|
|
}
|
|
|
|
|
2020-04-03 18:01:28 -04:00
|
|
|
/**********************************************************************************************************************************/
|
2019-08-21 11:41:36 -04:00
|
|
|
void
|
|
|
|
repoIsLocalVerify(void)
|
|
|
|
{
|
|
|
|
FUNCTION_TEST_VOID();
|
|
|
|
|
|
|
|
if (!repoIsLocal())
|
|
|
|
THROW_FMT(HostInvalidError, "%s command must be run on the repository host", cfgCommandName(cfgCommand()));
|
|
|
|
|
|
|
|
FUNCTION_TEST_RETURN_VOID();
|
|
|
|
}
|
|
|
|
|
2020-04-03 18:01:28 -04:00
|
|
|
/**********************************************************************************************************************************/
|
2019-07-17 14:09:50 -04:00
|
|
|
bool
|
2019-07-31 20:44:49 -04:00
|
|
|
pgIsLocal(unsigned int hostId)
|
2019-07-17 14:09:50 -04:00
|
|
|
{
|
2019-07-31 20:44:49 -04:00
|
|
|
FUNCTION_LOG_BEGIN(logLevelDebug);
|
|
|
|
FUNCTION_LOG_PARAM(UINT, hostId);
|
|
|
|
FUNCTION_LOG_END();
|
2019-07-17 14:09:50 -04:00
|
|
|
|
2019-12-04 19:31:39 -05:00
|
|
|
ASSERT(hostId > 0);
|
|
|
|
|
2019-07-31 20:44:49 -04:00
|
|
|
FUNCTION_LOG_RETURN(BOOL, !cfgOptionTest(cfgOptPgHost + hostId - 1));
|
2019-07-17 14:09:50 -04:00
|
|
|
}
|
|
|
|
|
2020-02-12 15:47:07 -07:00
|
|
|
/**********************************************************************************************************************************/
|
|
|
|
void
|
|
|
|
pgIsLocalVerify(void)
|
|
|
|
{
|
|
|
|
FUNCTION_TEST_VOID();
|
|
|
|
|
|
|
|
if (!pgIsLocal(1))
|
|
|
|
THROW_FMT(HostInvalidError, "%s command must be run on the " PG_NAME " host", cfgCommandName(cfgCommand()));
|
|
|
|
|
|
|
|
FUNCTION_TEST_RETURN_VOID();
|
|
|
|
}
|
|
|
|
|
2019-01-18 21:32:51 +02:00
|
|
|
/***********************************************************************************************************************************
|
2019-02-27 22:34:21 +02:00
|
|
|
Get the command line required for local protocol execution
|
2019-01-18 21:32:51 +02:00
|
|
|
***********************************************************************************************************************************/
|
|
|
|
static StringList *
|
2019-11-23 10:32:57 -05:00
|
|
|
protocolLocalParam(ProtocolStorageType protocolStorageType, unsigned int hostId, unsigned int protocolId)
|
2019-02-27 22:34:21 +02:00
|
|
|
{
|
|
|
|
FUNCTION_LOG_BEGIN(logLevelDebug);
|
|
|
|
FUNCTION_LOG_PARAM(ENUM, protocolStorageType);
|
2019-11-23 10:32:57 -05:00
|
|
|
FUNCTION_LOG_PARAM(UINT, hostId);
|
2019-02-27 22:34:21 +02:00
|
|
|
FUNCTION_LOG_PARAM(UINT, protocolId);
|
|
|
|
FUNCTION_LOG_END();
|
|
|
|
|
2019-12-04 19:31:39 -05:00
|
|
|
ASSERT(hostId > 0);
|
|
|
|
|
2019-02-27 22:34:21 +02:00
|
|
|
StringList *result = NULL;
|
|
|
|
|
|
|
|
MEM_CONTEXT_TEMP_BEGIN()
|
|
|
|
{
|
|
|
|
// Option replacements
|
|
|
|
KeyValue *optionReplace = kvNew();
|
|
|
|
|
|
|
|
// Add the process id -- used when more than one process will be called
|
2019-11-23 10:32:57 -05:00
|
|
|
kvPut(optionReplace, VARSTR(CFGOPT_PROCESS_STR), VARUINT(protocolId));
|
2019-02-27 22:34:21 +02:00
|
|
|
|
2019-11-23 10:32:57 -05:00
|
|
|
// Add the host id
|
|
|
|
kvPut(optionReplace, VARSTR(CFGOPT_HOST_ID_STR), VARUINT(hostId));
|
2019-02-27 22:34:21 +02:00
|
|
|
|
2020-01-09 11:56:03 -07:00
|
|
|
// Add the remote type
|
2020-01-08 18:59:02 -07:00
|
|
|
kvPut(optionReplace, VARSTR(CFGOPT_REMOTE_TYPE_STR), VARSTR(protocolStorageTypeStr(protocolStorageType)));
|
2019-02-27 22:34:21 +02:00
|
|
|
|
2019-03-16 15:00:02 +04:00
|
|
|
// Only enable file logging on the local when requested
|
|
|
|
kvPut(
|
2019-04-17 08:04:22 -04:00
|
|
|
optionReplace, VARSTR(CFGOPT_LOG_LEVEL_FILE_STR),
|
|
|
|
cfgOptionBool(cfgOptLogSubprocess) ? cfgOption(cfgOptLogLevelFile) : VARSTRDEF("off"));
|
2019-03-16 15:00:02 +04:00
|
|
|
|
|
|
|
// Always output errors on stderr for debugging purposes
|
2019-04-17 08:04:22 -04:00
|
|
|
kvPut(optionReplace, VARSTR(CFGOPT_LOG_LEVEL_STDERR_STR), VARSTRDEF("error"));
|
2019-03-16 15:00:02 +04:00
|
|
|
|
2020-01-15 12:24:58 -07:00
|
|
|
// Disable output to stdout since it is used by the protocol
|
|
|
|
kvPut(optionReplace, VARSTR(CFGOPT_LOG_LEVEL_CONSOLE_STR), VARSTRDEF("off"));
|
|
|
|
|
2020-01-17 13:29:49 -07:00
|
|
|
result = strLstMove(cfgExecParam(cfgCommand(), cfgCmdRoleLocal, optionReplace, true, false), memContextPrior());
|
2019-02-27 22:34:21 +02:00
|
|
|
}
|
|
|
|
MEM_CONTEXT_TEMP_END();
|
|
|
|
|
|
|
|
FUNCTION_LOG_RETURN(STRING_LIST, result);
|
|
|
|
}
|
|
|
|
|
2020-04-03 18:01:28 -04:00
|
|
|
/**********************************************************************************************************************************/
|
2019-02-27 22:34:21 +02:00
|
|
|
ProtocolClient *
|
2019-11-23 10:32:57 -05:00
|
|
|
protocolLocalGet(ProtocolStorageType protocolStorageType, unsigned int hostId, unsigned int protocolId)
|
2019-01-18 21:32:51 +02:00
|
|
|
{
|
2019-01-21 17:41:59 +02:00
|
|
|
FUNCTION_LOG_BEGIN(logLevelDebug);
|
2019-02-27 22:34:21 +02:00
|
|
|
FUNCTION_LOG_PARAM(ENUM, protocolStorageType);
|
2019-11-23 10:32:57 -05:00
|
|
|
FUNCTION_LOG_PARAM(UINT, hostId);
|
2019-02-27 22:34:21 +02:00
|
|
|
FUNCTION_LOG_PARAM(UINT, protocolId);
|
2019-01-21 17:41:59 +02:00
|
|
|
FUNCTION_LOG_END();
|
2019-01-18 21:32:51 +02:00
|
|
|
|
2019-12-04 19:31:39 -05:00
|
|
|
ASSERT(hostId > 0);
|
|
|
|
|
2019-02-27 22:34:21 +02:00
|
|
|
protocolHelperInit();
|
|
|
|
|
|
|
|
// Allocate the client cache
|
|
|
|
if (protocolHelper.clientLocalSize == 0)
|
|
|
|
{
|
|
|
|
MEM_CONTEXT_BEGIN(protocolHelper.memContext)
|
|
|
|
{
|
2019-12-05 22:34:38 -05:00
|
|
|
protocolHelper.clientLocalSize = cfgOptionUInt(cfgOptProcessMax) + 1;
|
2020-01-23 14:15:58 -07:00
|
|
|
protocolHelper.clientLocal = memNew(protocolHelper.clientLocalSize * sizeof(ProtocolHelperClient));
|
|
|
|
|
|
|
|
for (unsigned int clientIdx = 0; clientIdx < protocolHelper.clientLocalSize; clientIdx++)
|
|
|
|
protocolHelper.clientLocal[clientIdx] = (ProtocolHelperClient){.exec = NULL};
|
2019-02-27 22:34:21 +02:00
|
|
|
}
|
|
|
|
MEM_CONTEXT_END();
|
|
|
|
}
|
|
|
|
|
|
|
|
ASSERT(protocolId <= protocolHelper.clientLocalSize);
|
|
|
|
|
|
|
|
// Create protocol object
|
|
|
|
ProtocolHelperClient *protocolHelperClient = &protocolHelper.clientLocal[protocolId - 1];
|
|
|
|
|
|
|
|
if (protocolHelperClient->client == NULL)
|
|
|
|
{
|
|
|
|
MEM_CONTEXT_BEGIN(protocolHelper.memContext)
|
|
|
|
{
|
|
|
|
// Execute the protocol command
|
|
|
|
protocolHelperClient->exec = execNew(
|
2019-11-23 10:32:57 -05:00
|
|
|
cfgExe(), protocolLocalParam(protocolStorageType, hostId, protocolId),
|
2019-02-27 22:34:21 +02:00
|
|
|
strNewFmt(PROTOCOL_SERVICE_LOCAL "-%u process", protocolId),
|
|
|
|
(TimeMSec)(cfgOptionDbl(cfgOptProtocolTimeout) * 1000));
|
|
|
|
execOpen(protocolHelperClient->exec);
|
|
|
|
|
|
|
|
// Create protocol object
|
|
|
|
protocolHelperClient->client = protocolClientNew(
|
|
|
|
strNewFmt(PROTOCOL_SERVICE_LOCAL "-%u protocol", protocolId),
|
|
|
|
PROTOCOL_SERVICE_LOCAL_STR, execIoRead(protocolHelperClient->exec), execIoWrite(protocolHelperClient->exec));
|
|
|
|
|
|
|
|
protocolClientMove(protocolHelperClient->client, execMemContext(protocolHelperClient->exec));
|
|
|
|
}
|
|
|
|
MEM_CONTEXT_END();
|
|
|
|
}
|
|
|
|
|
|
|
|
FUNCTION_LOG_RETURN(PROTOCOL_CLIENT, protocolHelperClient->client);
|
|
|
|
}
|
|
|
|
|
|
|
|
/***********************************************************************************************************************************
|
|
|
|
Get the command line required for remote protocol execution
|
|
|
|
***********************************************************************************************************************************/
|
|
|
|
static StringList *
|
2019-07-31 20:44:49 -04:00
|
|
|
protocolRemoteParam(ProtocolStorageType protocolStorageType, unsigned int protocolId, unsigned int hostIdx)
|
2019-02-27 22:34:21 +02:00
|
|
|
{
|
|
|
|
FUNCTION_LOG_BEGIN(logLevelDebug);
|
|
|
|
FUNCTION_LOG_PARAM(ENUM, protocolStorageType);
|
|
|
|
FUNCTION_LOG_PARAM(UINT, protocolId);
|
2019-07-31 20:44:49 -04:00
|
|
|
FUNCTION_LOG_PARAM(UINT, hostIdx);
|
2019-02-27 22:34:21 +02:00
|
|
|
FUNCTION_LOG_END();
|
|
|
|
|
2019-07-17 14:09:50 -04:00
|
|
|
// Is this a repo remote?
|
|
|
|
bool isRepo = protocolStorageType == protocolStorageTypeRepo;
|
|
|
|
|
2019-01-18 21:32:51 +02:00
|
|
|
// Fixed parameters for ssh command
|
|
|
|
StringList *result = strLstNew();
|
|
|
|
strLstAddZ(result, "-o");
|
|
|
|
strLstAddZ(result, "LogLevel=error");
|
|
|
|
strLstAddZ(result, "-o");
|
|
|
|
strLstAddZ(result, "Compression=no");
|
|
|
|
strLstAddZ(result, "-o");
|
|
|
|
strLstAddZ(result, "PasswordAuthentication=no");
|
|
|
|
|
|
|
|
// Append port if specified
|
2019-07-17 14:09:50 -04:00
|
|
|
ConfigOption optHostPort = isRepo ? cfgOptRepoHostPort : cfgOptPgHostPort + hostIdx;
|
|
|
|
|
|
|
|
if (cfgOptionTest(optHostPort))
|
2019-01-18 21:32:51 +02:00
|
|
|
{
|
|
|
|
strLstAddZ(result, "-p");
|
2019-07-17 14:09:50 -04:00
|
|
|
strLstAdd(result, strNewFmt("%u", cfgOptionUInt(optHostPort)));
|
2019-01-18 21:32:51 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Append user/host
|
2019-07-17 14:09:50 -04:00
|
|
|
strLstAdd(
|
|
|
|
result,
|
|
|
|
strNewFmt(
|
|
|
|
"%s@%s", strPtr(cfgOptionStr(isRepo ? cfgOptRepoHostUser : cfgOptPgHostUser + hostIdx)),
|
|
|
|
strPtr(cfgOptionStr(isRepo ? cfgOptRepoHost : cfgOptPgHost + hostIdx))));
|
2019-01-18 21:32:51 +02:00
|
|
|
|
2019-02-27 22:34:21 +02:00
|
|
|
// Option replacements
|
2019-01-18 21:32:51 +02:00
|
|
|
KeyValue *optionReplace = kvNew();
|
|
|
|
|
|
|
|
// Replace config options with the host versions
|
2019-07-17 14:09:50 -04:00
|
|
|
unsigned int optConfig = isRepo ? cfgOptRepoHostConfig : cfgOptPgHostConfig + hostIdx;
|
|
|
|
|
2019-07-26 08:37:58 -04:00
|
|
|
kvPut(optionReplace, VARSTR(CFGOPT_CONFIG_STR), cfgOptionSource(optConfig) != cfgSourceDefault ? cfgOption(optConfig) : NULL);
|
2019-07-17 14:09:50 -04:00
|
|
|
|
|
|
|
unsigned int optConfigIncludePath = isRepo ? cfgOptRepoHostConfigIncludePath : cfgOptPgHostConfigIncludePath + hostIdx;
|
|
|
|
|
2019-07-26 08:37:58 -04:00
|
|
|
kvPut(
|
|
|
|
optionReplace, VARSTR(CFGOPT_CONFIG_INCLUDE_PATH_STR),
|
|
|
|
cfgOptionSource(optConfigIncludePath) != cfgSourceDefault ? cfgOption(optConfigIncludePath) : NULL);
|
2019-01-18 21:32:51 +02:00
|
|
|
|
2019-07-17 14:09:50 -04:00
|
|
|
unsigned int optConfigPath = isRepo ? cfgOptRepoHostConfigPath : cfgOptPgHostConfigPath + hostIdx;
|
2019-01-18 21:32:51 +02:00
|
|
|
|
2019-07-26 08:37:58 -04:00
|
|
|
kvPut(
|
|
|
|
optionReplace, VARSTR(CFGOPT_CONFIG_PATH_STR),
|
|
|
|
cfgOptionSource(optConfigPath) != cfgSourceDefault ? cfgOption(optConfigPath) : NULL);
|
2019-01-18 21:32:51 +02:00
|
|
|
|
2020-01-15 12:24:58 -07:00
|
|
|
// Update/remove repo/pg options that are sent to the remote
|
2020-05-21 16:09:23 -04:00
|
|
|
ConfigDefineCommand commandDefId = cfgCommandDefIdFromId(cfgCommand());
|
2020-01-15 12:24:58 -07:00
|
|
|
const String *repoHostPrefix = STR(cfgDefOptionName(cfgDefOptRepoHost));
|
|
|
|
const String *repoPrefix = strNewFmt("%s-", PROTOCOL_REMOTE_TYPE_REPO);
|
2020-01-09 12:20:13 -07:00
|
|
|
const String *pgHostPrefix = STR(cfgDefOptionName(cfgDefOptPgHost));
|
|
|
|
const String *pgPrefix = strNewFmt("%s-", PROTOCOL_REMOTE_TYPE_PG);
|
2019-07-17 14:09:50 -04:00
|
|
|
|
2020-01-09 12:20:13 -07:00
|
|
|
for (ConfigOption optionId = 0; optionId < CFG_OPTION_TOTAL; optionId++)
|
2019-07-17 14:09:50 -04:00
|
|
|
{
|
2020-05-21 16:09:23 -04:00
|
|
|
ConfigDefineOption optionDefId = cfgOptionDefIdFromId(optionId);
|
|
|
|
const String *optionDefName = STR(cfgDefOptionName(optionDefId));
|
2020-01-15 12:24:58 -07:00
|
|
|
bool remove = false;
|
2020-01-09 12:20:13 -07:00
|
|
|
|
2020-01-15 12:24:58 -07:00
|
|
|
// Remove repo host options that are not needed on the remote. The remote is not expecting to see host settings and it
|
|
|
|
// could get confused about the locality of the repo, i.e. local or remote.
|
|
|
|
if (strBeginsWith(optionDefName, repoHostPrefix))
|
|
|
|
{
|
|
|
|
remove = true;
|
|
|
|
}
|
|
|
|
// Remove repo options when the remote type is pg since they won't be used
|
|
|
|
else if (strBeginsWith(optionDefName, repoPrefix))
|
|
|
|
{
|
|
|
|
if (protocolStorageType == protocolStorageTypePg)
|
|
|
|
remove = true;
|
|
|
|
}
|
|
|
|
// Remove pg host options that are not needed on the remote. The remote is not expecting to see host settings and it could
|
|
|
|
// get confused about the locality of pg, i.e. local or remote.
|
|
|
|
else if (strBeginsWith(optionDefName, pgHostPrefix))
|
|
|
|
{
|
|
|
|
remove = true;
|
|
|
|
}
|
|
|
|
else if (strBeginsWith(optionDefName, pgPrefix))
|
2020-01-09 12:20:13 -07:00
|
|
|
{
|
2020-05-21 16:09:23 -04:00
|
|
|
// Remove unrequired/defaulted pg options when the remote type is repo since they won't be used
|
2020-01-15 12:24:58 -07:00
|
|
|
if (protocolStorageType == protocolStorageTypeRepo)
|
|
|
|
{
|
2020-05-21 16:09:23 -04:00
|
|
|
remove = !cfgDefOptionRequired(commandDefId, optionDefId) || cfgDefOptionDefault(commandDefId, optionDefId) != NULL;
|
2020-01-15 12:24:58 -07:00
|
|
|
}
|
|
|
|
// Else move/remove pg options with index > 0 since they won't be used
|
|
|
|
else if (cfgOptionIndex(optionId) > 0)
|
2020-01-09 12:20:13 -07:00
|
|
|
{
|
2020-01-15 12:24:58 -07:00
|
|
|
// If the option index matches the host-id then this is a pg option that the remote needs. Since the remote expects
|
|
|
|
// to find pg options in index 0, copy the option to index 0.
|
|
|
|
if (cfgOptionIndex(optionId) == hostIdx)
|
|
|
|
{
|
|
|
|
kvPut(
|
|
|
|
optionReplace, VARSTRZ(cfgOptionName(optionId - hostIdx)),
|
|
|
|
cfgOptionSource(optionId) != cfgSourceDefault ? cfgOption(optionId) : NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Remove pg options that are not needed on the remote. The remote is only going to look at index 0 so the options
|
|
|
|
// in higher indexes will not be used and just add clutter which makes debugging harder.
|
|
|
|
remove = true;
|
2020-01-09 12:20:13 -07:00
|
|
|
}
|
2020-01-15 12:24:58 -07:00
|
|
|
}
|
2020-01-09 12:20:13 -07:00
|
|
|
|
2020-01-15 12:24:58 -07:00
|
|
|
// Remove options that have been marked for removal if they are not already null or invalid. This is more efficient because
|
|
|
|
// cfgExecParam() won't have to search through as large a list looking for overrides.
|
|
|
|
if (remove && cfgOptionTest(optionId))
|
2020-01-09 12:20:13 -07:00
|
|
|
kvPut(optionReplace, VARSTRZ(cfgOptionName(optionId)), NULL);
|
2019-07-17 14:09:50 -04:00
|
|
|
}
|
|
|
|
|
2020-01-15 12:24:58 -07:00
|
|
|
// Don't pass host-id to the remote. The host will always be in index 0.
|
|
|
|
kvPut(optionReplace, VARSTR(CFGOPT_HOST_ID_STR), NULL);
|
2019-01-18 21:32:51 +02:00
|
|
|
|
2019-02-28 09:51:19 +02:00
|
|
|
// Add the process id (or use the current process id if it is valid)
|
|
|
|
if (!cfgOptionTest(cfgOptProcess))
|
2019-04-17 08:04:22 -04:00
|
|
|
kvPut(optionReplace, VARSTR(CFGOPT_PROCESS_STR), VARINT((int)protocolId));
|
2019-01-18 21:32:51 +02:00
|
|
|
|
2019-03-16 15:00:02 +04:00
|
|
|
// Don't pass log-path or lock-path since these are host specific
|
2019-04-17 08:04:22 -04:00
|
|
|
kvPut(optionReplace, VARSTR(CFGOPT_LOG_PATH_STR), NULL);
|
|
|
|
kvPut(optionReplace, VARSTR(CFGOPT_LOCK_PATH_STR), NULL);
|
2019-03-16 15:00:02 +04:00
|
|
|
|
2020-01-15 12:24:58 -07:00
|
|
|
// ??? Don't pass restore options which the remote doesn't need and are likely to contain spaces because they might get mangled
|
|
|
|
// on the way to the remote depending on how SSH is set up on the server. This code should be removed when option passing with
|
|
|
|
// spaces is resolved.
|
|
|
|
kvPut(optionReplace, VARSTR(CFGOPT_TYPE_STR), NULL);
|
|
|
|
kvPut(optionReplace, VARSTR(CFGOPT_TARGET_STR), NULL);
|
|
|
|
kvPut(optionReplace, VARSTR(CFGOPT_TARGET_EXCLUSIVE_STR), NULL);
|
|
|
|
kvPut(optionReplace, VARSTR(CFGOPT_TARGET_ACTION_STR), NULL);
|
|
|
|
kvPut(optionReplace, VARSTR(CFGOPT_TARGET_STR), NULL);
|
|
|
|
kvPut(optionReplace, VARSTR(CFGOPT_TARGET_TIMELINE_STR), NULL);
|
|
|
|
kvPut(optionReplace, VARSTR(CFGOPT_RECOVERY_OPTION_STR), NULL);
|
|
|
|
|
2019-03-16 15:00:02 +04:00
|
|
|
// Only enable file logging on the remote when requested
|
|
|
|
kvPut(
|
2019-04-17 08:04:22 -04:00
|
|
|
optionReplace, VARSTR(CFGOPT_LOG_LEVEL_FILE_STR),
|
|
|
|
cfgOptionBool(cfgOptLogSubprocess) ? cfgOption(cfgOptLogLevelFile) : VARSTRDEF("off"));
|
2019-03-16 15:00:02 +04:00
|
|
|
|
|
|
|
// Always output errors on stderr for debugging purposes
|
2019-04-17 08:04:22 -04:00
|
|
|
kvPut(optionReplace, VARSTR(CFGOPT_LOG_LEVEL_STDERR_STR), VARSTRDEF("error"));
|
2019-03-16 15:00:02 +04:00
|
|
|
|
2020-01-15 12:24:58 -07:00
|
|
|
// Disable output to stdout since it is used by the protocol
|
|
|
|
kvPut(optionReplace, VARSTR(CFGOPT_LOG_LEVEL_CONSOLE_STR), VARSTRDEF("off"));
|
|
|
|
|
2020-01-09 11:56:03 -07:00
|
|
|
// Add the remote type
|
2020-01-08 18:59:02 -07:00
|
|
|
kvPut(optionReplace, VARSTR(CFGOPT_REMOTE_TYPE_STR), VARSTR(protocolStorageTypeStr(protocolStorageType)));
|
2019-01-18 21:32:51 +02:00
|
|
|
|
2020-01-15 12:24:58 -07:00
|
|
|
StringList *commandExec = cfgExecParam(cfgCommand(), cfgCmdRoleRemote, optionReplace, false, true);
|
2019-07-17 14:09:50 -04:00
|
|
|
strLstInsert(commandExec, 0, cfgOptionStr(isRepo ? cfgOptRepoHostCmd : cfgOptPgHostCmd + hostIdx));
|
2019-01-18 21:32:51 +02:00
|
|
|
strLstAdd(result, strLstJoin(commandExec, " "));
|
|
|
|
|
2019-01-21 17:41:59 +02:00
|
|
|
FUNCTION_LOG_RETURN(STRING_LIST, result);
|
2019-01-18 21:32:51 +02:00
|
|
|
}
|
|
|
|
|
2020-04-03 18:01:28 -04:00
|
|
|
/**********************************************************************************************************************************/
|
2019-01-18 21:32:51 +02:00
|
|
|
ProtocolClient *
|
2019-07-31 20:44:49 -04:00
|
|
|
protocolRemoteGet(ProtocolStorageType protocolStorageType, unsigned int hostId)
|
2019-01-18 21:32:51 +02:00
|
|
|
{
|
2019-01-21 17:41:59 +02:00
|
|
|
FUNCTION_LOG_BEGIN(logLevelDebug);
|
2019-02-27 22:34:21 +02:00
|
|
|
FUNCTION_LOG_PARAM(ENUM, protocolStorageType);
|
2019-07-31 20:44:49 -04:00
|
|
|
FUNCTION_LOG_PARAM(UINT, hostId);
|
2019-01-21 17:41:59 +02:00
|
|
|
FUNCTION_LOG_END();
|
2019-01-18 21:32:51 +02:00
|
|
|
|
2019-12-04 19:31:39 -05:00
|
|
|
ASSERT(hostId > 0);
|
|
|
|
|
2019-07-17 14:09:50 -04:00
|
|
|
// Is this a repo remote?
|
|
|
|
bool isRepo = protocolStorageType == protocolStorageTypeRepo;
|
|
|
|
|
2019-02-27 22:34:21 +02:00
|
|
|
protocolHelperInit();
|
|
|
|
|
|
|
|
// Allocate the client cache
|
|
|
|
if (protocolHelper.clientRemoteSize == 0)
|
2019-01-18 21:32:51 +02:00
|
|
|
{
|
2019-02-27 22:34:21 +02:00
|
|
|
MEM_CONTEXT_BEGIN(protocolHelper.memContext)
|
2019-01-18 21:32:51 +02:00
|
|
|
{
|
2019-07-17 14:09:50 -04:00
|
|
|
// The number of remotes allowed is the greater of allowed repo or pg configs + 1 (0 is reserved for connections from
|
2019-02-27 22:34:21 +02:00
|
|
|
// the main process). Since these are static and only one will be true it presents a problem for coverage. We think
|
|
|
|
// that pg remotes will always be greater but we'll protect that assumption with an assertion.
|
|
|
|
ASSERT(cfgDefOptionIndexTotal(cfgDefOptPgPath) >= cfgDefOptionIndexTotal(cfgDefOptRepoPath));
|
|
|
|
|
2019-04-29 14:23:37 -04:00
|
|
|
protocolHelper.clientRemoteSize = cfgDefOptionIndexTotal(cfgDefOptPgPath) + 1;
|
2020-01-23 14:15:58 -07:00
|
|
|
protocolHelper.clientRemote = memNew(protocolHelper.clientRemoteSize * sizeof(ProtocolHelperClient));
|
|
|
|
|
|
|
|
for (unsigned int clientIdx = 0; clientIdx < protocolHelper.clientRemoteSize; clientIdx++)
|
|
|
|
protocolHelper.clientRemote[clientIdx] = (ProtocolHelperClient){.exec = NULL};
|
2019-01-18 21:32:51 +02:00
|
|
|
}
|
|
|
|
MEM_CONTEXT_END();
|
|
|
|
}
|
|
|
|
|
2019-07-31 20:44:49 -04:00
|
|
|
// Determine protocol id for the remote. If the process option is set then use that since we want the remote protocol id to
|
|
|
|
// match the local protocol id. Otherwise set to 0 since the remote is being started from a main process and there should only
|
|
|
|
// be one remote per host.
|
2019-02-28 09:51:19 +02:00
|
|
|
unsigned int protocolId = 0;
|
2019-07-31 20:44:49 -04:00
|
|
|
|
|
|
|
// Use hostId to determine where to cache to remote
|
|
|
|
unsigned int protocolIdx = hostId - 1;
|
2019-02-28 09:51:19 +02:00
|
|
|
|
|
|
|
if (cfgOptionTest(cfgOptProcess))
|
2019-04-19 11:40:39 -04:00
|
|
|
protocolId = cfgOptionUInt(cfgOptProcess);
|
2019-02-28 09:51:19 +02:00
|
|
|
|
2019-04-29 16:10:27 -04:00
|
|
|
CHECK(protocolIdx < protocolHelper.clientRemoteSize);
|
2019-02-27 22:34:21 +02:00
|
|
|
|
2019-01-18 21:32:51 +02:00
|
|
|
// Create protocol object
|
2019-04-29 16:10:27 -04:00
|
|
|
ProtocolHelperClient *protocolHelperClient = &protocolHelper.clientRemote[protocolIdx];
|
2019-02-27 22:34:21 +02:00
|
|
|
|
|
|
|
if (protocolHelperClient->client == NULL)
|
2019-01-18 21:32:51 +02:00
|
|
|
{
|
|
|
|
MEM_CONTEXT_BEGIN(protocolHelper.memContext)
|
|
|
|
{
|
2019-07-31 20:44:49 -04:00
|
|
|
unsigned int optHost = isRepo ? cfgOptRepoHost : cfgOptPgHost + hostId - 1;
|
2019-07-17 14:09:50 -04:00
|
|
|
|
2019-01-18 21:32:51 +02:00
|
|
|
// Execute the protocol command
|
2019-02-27 22:34:21 +02:00
|
|
|
protocolHelperClient->exec = execNew(
|
2019-07-31 20:44:49 -04:00
|
|
|
cfgOptionStr(cfgOptCmdSsh), protocolRemoteParam(protocolStorageType, protocolId, hostId - 1),
|
2019-07-17 14:09:50 -04:00
|
|
|
strNewFmt(PROTOCOL_SERVICE_REMOTE "-%u process on '%s'", protocolId, strPtr(cfgOptionStr(optHost))),
|
2019-01-18 21:32:51 +02:00
|
|
|
(TimeMSec)(cfgOptionDbl(cfgOptProtocolTimeout) * 1000));
|
2019-02-27 22:34:21 +02:00
|
|
|
execOpen(protocolHelperClient->exec);
|
2019-01-18 21:32:51 +02:00
|
|
|
|
|
|
|
// Create protocol object
|
2019-02-27 22:34:21 +02:00
|
|
|
protocolHelperClient->client = protocolClientNew(
|
2019-07-17 14:09:50 -04:00
|
|
|
strNewFmt(PROTOCOL_SERVICE_REMOTE "-%u protocol on '%s'", protocolId, strPtr(cfgOptionStr(optHost))),
|
2019-02-27 22:34:21 +02:00
|
|
|
PROTOCOL_SERVICE_REMOTE_STR, execIoRead(protocolHelperClient->exec), execIoWrite(protocolHelperClient->exec));
|
2019-01-18 21:32:51 +02:00
|
|
|
|
2019-02-19 20:57:38 +02:00
|
|
|
// Get cipher options from the remote if none are locally configured
|
2019-07-17 14:09:50 -04:00
|
|
|
if (isRepo && strEq(cfgOptionStr(cfgOptRepoCipherType), CIPHER_TYPE_NONE_STR))
|
2019-02-19 20:57:38 +02:00
|
|
|
{
|
|
|
|
// Options to query
|
|
|
|
VariantList *param = varLstNew();
|
2019-04-12 09:03:34 -04:00
|
|
|
varLstAdd(param, varNewStr(CFGOPT_REPO1_CIPHER_TYPE_STR));
|
|
|
|
varLstAdd(param, varNewStr(CFGOPT_REPO1_CIPHER_PASS_STR));
|
2019-02-19 20:57:38 +02:00
|
|
|
|
2019-02-27 22:34:21 +02:00
|
|
|
VariantList *optionList = configProtocolOption(protocolHelperClient->client, param);
|
2019-02-19 20:57:38 +02:00
|
|
|
|
|
|
|
if (!strEq(varStr(varLstGet(optionList, 0)), CIPHER_TYPE_NONE_STR))
|
|
|
|
{
|
|
|
|
cfgOptionSet(cfgOptRepoCipherType, cfgSourceConfig, varLstGet(optionList, 0));
|
|
|
|
cfgOptionSet(cfgOptRepoCipherPass, cfgSourceConfig, varLstGet(optionList, 1));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-27 22:34:21 +02:00
|
|
|
protocolClientMove(protocolHelperClient->client, execMemContext(protocolHelperClient->exec));
|
2019-01-18 21:32:51 +02:00
|
|
|
}
|
|
|
|
MEM_CONTEXT_END();
|
|
|
|
}
|
|
|
|
|
2019-02-27 22:34:21 +02:00
|
|
|
FUNCTION_LOG_RETURN(PROTOCOL_CLIENT, protocolHelperClient->client);
|
2019-01-18 21:32:51 +02:00
|
|
|
}
|
|
|
|
|
2019-12-07 17:48:53 -05:00
|
|
|
/**********************************************************************************************************************************/
|
|
|
|
void
|
|
|
|
protocolRemoteFree(unsigned int hostId)
|
|
|
|
{
|
|
|
|
FUNCTION_LOG_BEGIN(logLevelDebug);
|
|
|
|
FUNCTION_LOG_PARAM(UINT, hostId);
|
|
|
|
FUNCTION_LOG_END();
|
|
|
|
|
|
|
|
ASSERT(hostId > 0);
|
|
|
|
|
|
|
|
if (protocolHelper.clientRemote != NULL)
|
|
|
|
{
|
|
|
|
ProtocolHelperClient *protocolHelperClient = &protocolHelper.clientRemote[hostId - 1];
|
|
|
|
|
|
|
|
if (protocolHelperClient->client != NULL)
|
|
|
|
{
|
|
|
|
protocolClientFree(protocolHelperClient->client);
|
|
|
|
execFree(protocolHelperClient->exec);
|
|
|
|
|
|
|
|
protocolHelperClient->client = NULL;
|
|
|
|
protocolHelperClient->exec = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
FUNCTION_LOG_RETURN_VOID();
|
|
|
|
}
|
|
|
|
|
2020-04-03 18:01:28 -04:00
|
|
|
/**********************************************************************************************************************************/
|
2019-03-27 20:59:28 +00:00
|
|
|
void
|
|
|
|
protocolKeepAlive(void)
|
|
|
|
{
|
|
|
|
FUNCTION_LOG_VOID(logLevelTrace);
|
|
|
|
|
|
|
|
if (protocolHelper.memContext != NULL)
|
|
|
|
{
|
|
|
|
for (unsigned int clientIdx = 0; clientIdx < protocolHelper.clientRemoteSize; clientIdx++)
|
|
|
|
{
|
|
|
|
if (protocolHelper.clientRemote[clientIdx].client != NULL)
|
|
|
|
protocolClientNoOp(protocolHelper.clientRemote[clientIdx].client);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
FUNCTION_LOG_RETURN_VOID();
|
|
|
|
}
|
|
|
|
|
2019-11-23 10:22:11 -05:00
|
|
|
/***********************************************************************************************************************************
|
2020-04-03 18:15:32 -04:00
|
|
|
Getters/Setters
|
2019-11-23 10:22:11 -05:00
|
|
|
***********************************************************************************************************************************/
|
|
|
|
ProtocolStorageType
|
|
|
|
protocolStorageTypeEnum(const String *type)
|
|
|
|
{
|
|
|
|
FUNCTION_TEST_BEGIN();
|
|
|
|
FUNCTION_TEST_PARAM(STRING, type);
|
|
|
|
FUNCTION_TEST_END();
|
|
|
|
|
|
|
|
ASSERT(type != NULL);
|
|
|
|
|
2020-01-08 18:59:02 -07:00
|
|
|
if (strEq(type, PROTOCOL_REMOTE_TYPE_PG_STR))
|
2019-11-23 10:22:11 -05:00
|
|
|
FUNCTION_TEST_RETURN(protocolStorageTypePg);
|
2020-01-08 18:59:02 -07:00
|
|
|
else if (strEq(type, PROTOCOL_REMOTE_TYPE_REPO_STR))
|
2019-11-23 10:22:11 -05:00
|
|
|
FUNCTION_TEST_RETURN(protocolStorageTypeRepo);
|
|
|
|
|
|
|
|
THROW_FMT(AssertError, "invalid protocol storage type '%s'", strPtr(type));
|
|
|
|
}
|
|
|
|
|
|
|
|
const String *
|
|
|
|
protocolStorageTypeStr(ProtocolStorageType type)
|
|
|
|
{
|
|
|
|
FUNCTION_TEST_BEGIN();
|
|
|
|
FUNCTION_TEST_PARAM(ENUM, type);
|
|
|
|
FUNCTION_TEST_END();
|
|
|
|
|
|
|
|
switch (type)
|
|
|
|
{
|
|
|
|
case protocolStorageTypePg:
|
2020-01-08 18:59:02 -07:00
|
|
|
FUNCTION_TEST_RETURN(PROTOCOL_REMOTE_TYPE_PG_STR);
|
2019-11-23 10:22:11 -05:00
|
|
|
|
|
|
|
case protocolStorageTypeRepo:
|
2020-01-08 18:59:02 -07:00
|
|
|
FUNCTION_TEST_RETURN(PROTOCOL_REMOTE_TYPE_REPO_STR);
|
2019-11-23 10:22:11 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
THROW_FMT(AssertError, "invalid protocol storage type %u", type);
|
|
|
|
}
|
|
|
|
|
2020-04-03 18:01:28 -04:00
|
|
|
/**********************************************************************************************************************************/
|
2019-01-18 21:32:51 +02:00
|
|
|
void
|
|
|
|
protocolFree(void)
|
|
|
|
{
|
2019-01-21 17:41:59 +02:00
|
|
|
FUNCTION_LOG_VOID(logLevelTrace);
|
2019-01-18 21:32:51 +02:00
|
|
|
|
2019-02-27 22:34:21 +02:00
|
|
|
if (protocolHelper.memContext != NULL)
|
2019-01-18 21:32:51 +02:00
|
|
|
{
|
2019-02-27 22:34:21 +02:00
|
|
|
// Free remotes
|
2019-12-07 17:48:53 -05:00
|
|
|
for (unsigned int clientIdx = 0; clientIdx < protocolHelper.clientRemoteSize; clientIdx++)
|
|
|
|
protocolRemoteFree(clientIdx + 1);
|
2019-02-27 22:34:21 +02:00
|
|
|
|
|
|
|
// Free locals
|
|
|
|
for (unsigned int clientIdx = 0; clientIdx < protocolHelper.clientLocalSize; clientIdx++)
|
|
|
|
{
|
|
|
|
ProtocolHelperClient *protocolHelperClient = &protocolHelper.clientLocal[clientIdx];
|
|
|
|
|
|
|
|
if (protocolHelperClient->client != NULL)
|
|
|
|
{
|
|
|
|
protocolClientFree(protocolHelperClient->client);
|
|
|
|
execFree(protocolHelperClient->exec);
|
|
|
|
|
2020-01-23 14:15:58 -07:00
|
|
|
*protocolHelperClient = (ProtocolHelperClient){.exec = NULL};
|
2019-02-27 22:34:21 +02:00
|
|
|
}
|
|
|
|
}
|
2019-01-18 21:32:51 +02:00
|
|
|
}
|
|
|
|
|
2019-01-21 17:41:59 +02:00
|
|
|
FUNCTION_LOG_RETURN_VOID();
|
2019-01-18 21:32:51 +02:00
|
|
|
}
|