* Copyright (c) 2021 Huawei Technologies Co.,Ltd.
*
* CM is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
*
* http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
* -------------------------------------------------------------------------
*
* ctl_guc.cpp
*
*
* IDENTIFICATION
* src/cm_ctl/ctl_guc.cpp
*
* -------------------------------------------------------------------------
*/
#include <string>
#include <termios.h>
#include <signal.h>
#include <stdlib.h>
#include <sys/unistd.h>
#include "cm_misc.h"
#include "securec_check.h"
#include "cm/libpq-fe.h"
#include "ctl_common.h"
static const int MAX_PARAM_VALUE_LEN = 2048;
static const int CONF_COMMAND_LEN = 16;
static const int NODE_TYPE_LEN = 32;
static const int KEY_LEN = 16;
static char g_pidFile[CM_PATH_LENGTH];
static char g_tmpFile[CM_PATH_LENGTH];
static char g_confFile[CM_PATH_LENGTH];
extern char g_appPath[MAXPGPATH];
extern char mpp_env_separate_file[MAXPGPATH];
extern CtlCommand ctl_command;
static status_t CheckGucOption(const GucOption &gucCtx);
static inline void SkipSpace(char *&ptr)
{
if (ptr == NULL) {
write_runlog(ERROR, "ptr is NULL.\n");
return;
}
while (isspace((unsigned char)*ptr)) {
++ptr;
}
}
status_t CheckGucSetParameter(const CtlOption *ctx)
{
if (ctx->guc.parameter == NULL) {
write_runlog2(ERROR, errcode(ERRCODE_PARAMETER_FAILURE),
errmsg("The guc set option parameter is not specified."),
errdetail("N/A"), errmodule(MOD_CMCTL),
errcause("The guc set parameter is NULL."),
erraction("Please check the cmdline entered by the user."));
return CM_ERROR;
}
if (ctx->guc.value == NULL) {
write_runlog2(ERROR, errcode(ERRCODE_PARAMETER_FAILURE),
errmsg("The guc set option parameter is not specified."),
errdetail("N/A"), errmodule(MOD_CMCTL),
errcause("The guc set value is NULL."),
erraction("Please check the cmdline entered by the user."));
return CM_ERROR;
}
return CM_SUCCESS;
}
status_t CheckConfigFileStatus(struct stat statBuf, struct stat tmpBuf)
{
if ((lstat(g_confFile, &statBuf) != 0) && (lstat(g_tmpFile, &tmpBuf) != 0)) {
char *pchBaseName = strrchr(g_confFile, '/');
if (pchBaseName == NULL) {
pchBaseName = g_confFile;
} else {
++pchBaseName;
}
write_runlog(ERROR, "cm_ctl: %s does not exist.\n", pchBaseName);
return CM_ERROR;
}
return CM_SUCCESS;
}
static bool IsLineCommented(const char *optLine)
{
if (optLine == NULL) {
return false;
}
char *tmp = (char*)(optLine);
while (isspace((unsigned char)*tmp)) {
++tmp;
}
if (*tmp == '#') {
return true;
}
return false;
}
static bool IsMatchParameterName(char *optLine, const char *paraName, size_t &valueOffset, size_t &valueLength)
{
char *ptr = optLine;
char *valuePtr = NULL;
size_t paraLen = (size_t)strlen(paraName);
SkipSpace(ptr);
if (*ptr == '#') {
++ptr;
}
SkipSpace(ptr);
if (strncmp(ptr, paraName, paraLen) != 0) {
return false;
}
ptr += paraLen;
SkipSpace(ptr);
if (*ptr != '=') {
return false;
}
++ptr;
SkipSpace(ptr);
if (strlen(ptr) != 0) {
valuePtr = ptr + 1;
while ((*valuePtr != '\n') || (*valuePtr != '#')) {
if (isspace((unsigned char) *valuePtr) != 0) {
break;
}
++valuePtr;
}
}
valueOffset = (size_t)(ptr - optLine);
valueLength = (valuePtr == NULL) ? 0 : (size_t)(valuePtr - ptr);
return true;
}
static void PrintResults(bool isSuccess, const CtlOption *ctx)
{
errno_t rc1 = 0;
errno_t rc2 = 0;
char nodeType[NODE_TYPE_LEN];
char gucType[CONF_COMMAND_LEN];
char cmNodeType[NODE_TYPE_LEN];
if (isSuccess && ctx->guc.gucCommand == LIST_CONF_COMMAND) {
return;
}
switch (ctx->guc.nodeType) {
case NODE_TYPE_AGENT:
rc1 = strcpy_s(nodeType, NODE_TYPE_LEN, "cm_agent.conf");
rc2 = strcpy_s(cmNodeType, NODE_TYPE_LEN, "agent");
break;
case NODE_TYPE_SERVER:
rc1 = strcpy_s(nodeType, NODE_TYPE_LEN, "cm_server.conf");
rc2 = strcpy_s(cmNodeType, NODE_TYPE_LEN, "server");
break;
case NODE_TYPE_UNDEF:
default:
rc1 = strcpy_s(nodeType, NODE_TYPE_LEN, "unknown");
rc2 = strcpy_s(cmNodeType, NODE_TYPE_LEN, "unknown");
break;
}
securec_check_errno(rc1, (void)rc1);
securec_check_errno(rc2, (void)rc2);
switch (ctx->guc.gucCommand) {
case SET_CONF_COMMAND:
rc1 = strcpy_s(gucType, CONF_COMMAND_LEN, "set");
break;
case RELOAD_CONF_COMMAND:
rc1 = strcpy_s(gucType, CONF_COMMAND_LEN, "reload");
break;
case LIST_CONF_COMMAND:
rc1 = strcpy_s(gucType, CONF_COMMAND_LEN, "list");
break;
case UNKNOWN_COMMAND:
default:
break;
}
securec_check_errno(rc1, (void)rc1);
if (isSuccess) {
write_runlog(LOG, "%s %s success.\n", gucType, nodeType);
if (ctl_command == CM_SET_COMMAND) {
write_runlog(LOG, "HINT: For the setting to take effect, you should execute \'cm_ctl reload --param --%s\'.\n",
cmNodeType);
}
return;
}
write_runlog(ERROR, "%s %s fail.\n", gucType, nodeType);
return;
}
static void GetInstanceConfigfile(const NodeType &type, const char* dataDir)
{
errno_t rc;
switch (type) {
case NODE_TYPE_AGENT:
rc = snprintf_s(g_pidFile, CM_PATH_LENGTH, CM_PATH_LENGTH - 1, "%s/cm_agent.pid", dataDir);
securec_check_intval(rc, (void)rc);
rc = snprintf_s(g_confFile, CM_PATH_LENGTH, CM_PATH_LENGTH - 1, "%s/cm_agent.conf", dataDir);
securec_check_intval(rc, (void)rc);
rc = snprintf_s(g_tmpFile, CM_PATH_LENGTH, CM_PATH_LENGTH - 1, "%s/%s", dataDir, "cm_agent.conf.bak");
securec_check_intval(rc, (void)rc);
break;
case NODE_TYPE_SERVER:
rc = snprintf_s(g_pidFile, CM_PATH_LENGTH, CM_PATH_LENGTH - 1, "%s/cm_server.pid", dataDir);
securec_check_intval(rc, (void)rc);
rc = snprintf_s(g_confFile, CM_PATH_LENGTH, CM_PATH_LENGTH - 1, "%s/cm_server.conf", dataDir);
securec_check_intval(rc, (void)rc);
rc = snprintf_s(g_tmpFile, CM_PATH_LENGTH, CM_PATH_LENGTH - 1, "%s/%s", dataDir, "cm_server.conf.bak");
securec_check_intval(rc, (void)rc);
break;
default:
break;
}
}
static int GetLinesIndex(char **optLines, const char *parameter, size_t &valueOffset, size_t &valueLength)
{
int matchTimes = 0;
int targetLine = 0;
if (parameter == NULL) {
return -1;
}
for (int i = 0; optLines[i] != NULL; ++i) {
if (!IsLineCommented(optLines[i])) {
if (IsMatchParameterName(optLines[i], parameter, valueOffset, valueLength)) {
++matchTimes;
targetLine = i;
}
}
}
if (matchTimes > 0) {
if (matchTimes > 1) {
write_runlog(LOG, "WARNING: There are %d \'%s\' commented in conf, and only the "
"last one in %dth line will be set and used.\n",
matchTimes, parameter, (targetLine + 1));
}
return targetLine;
}
matchTimes = 0;
for (int i = 0; optLines[i] != NULL; ++i) {
if (IsLineCommented(optLines[i])) {
if (IsMatchParameterName(optLines[i], parameter, valueOffset, valueLength)) {
++matchTimes;
targetLine = i;
}
}
}
if (matchTimes > 0) {
return targetLine;
}
return -1;
}
static const char *GetCtlCommandType(const GucCommand &command)
{
switch (command) {
case SET_CONF_COMMAND:
return "set";
case RELOAD_CONF_COMMAND:
return "reload";
case LIST_CONF_COMMAND:
return "list";
default:
break;
}
return NULL;
}
static const char *GetInstanceType(const NodeType &type)
{
switch (type) {
case NODE_TYPE_SERVER:
return "--server";
case NODE_TYPE_AGENT:
return "--agent";
default:
return " ";
}
}
static void GetRemoteGucCommand(const CtlOption *ctx, char *cmd, size_t cmdLen)
{
int ret;
char nodeIdStr[CONF_COMMAND_LEN];
size_t curLen;
ret = snprintf_s(nodeIdStr, sizeof(nodeIdStr), sizeof(nodeIdStr) - 1, "%u", ctx->comm.nodeId);
securec_check_intval(ret, (void)ret);
ret = snprintf_s(cmd, cmdLen, cmdLen - 1, "%s/bin/%s %s --param %s -n %s ", g_appPath, CM_CTL_BIN_NAME,
GetCtlCommandType(ctx->guc.gucCommand), GetInstanceType(ctx->guc.nodeType), nodeIdStr);
securec_check_intval(ret, (void)ret);
curLen = (size_t)ret;
if (ctx->guc.gucCommand != SET_CONF_COMMAND || ctx->guc.value == NULL || ctx->guc.parameter == NULL) {
return;
}
if (strcmp(ctx->guc.parameter, "event_triggers") != 0) {
ret = snprintf_s((cmd + curLen), (cmdLen - curLen), ((cmdLen - curLen) - 1),
SYSTEMQUOTE "-k %s=\\\"%s\\\" " SYSTEMQUOTE, ctx->guc.parameter, ctx->guc.value);
securec_check_intval(ret, (void)ret);
} else {
const char *value = ctx->guc.value;
char valueCopy[cmdLen] = {0};
int j = 0;
for (size_t i = 0; i < strlen(value); ++i) {
if (value[i] == '"') {
valueCopy[j++] = '\\';
valueCopy[j++] = '\\';
valueCopy[j++] = '\\';
}
valueCopy[j++] = value[i];
}
ret = snprintf_s((cmd + curLen), (cmdLen - curLen), ((cmdLen - curLen) - 1),
SYSTEMQUOTE "-k %s=\\\"%s\\\" " SYSTEMQUOTE, ctx->guc.parameter, valueCopy);
securec_check_intval(ret, (void)ret);
}
}
static void PrintOneParameterAndValue(char *line)
{
char *ptr = line;
if (line == NULL) {
return;
}
string parameter;
string value;
SkipSpace(ptr);
parameter.clear();
while ((ptr != NULL) && (*ptr != '=') && !isspace((unsigned char)*ptr)) {
parameter.push_back(*ptr);
++ptr;
}
SkipSpace(ptr);
if (*ptr == '=') {
++ptr;
} else {
return;
}
SkipSpace(ptr);
value.clear();
while ((ptr != NULL) && (*ptr != '#') && !isspace((unsigned char)*ptr)) {
value.push_back(*ptr);
++ptr;
}
if (!parameter.empty() && !value.empty()) {
(void)printf(_("%s = %s\n"), parameter.c_str(), value.c_str());
}
}
static void PrintValueAndParameter(char **lines)
{
(void)printf(_("\n[conf of node(%u)]\n"), g_currentNode->node);
for (int i = 0; lines[i] != NULL; ++i) {
if (IsLineCommented(lines[i]) || (strcmp(lines[i], "\n") == 0)) {
continue;
}
PrintOneParameterAndValue(lines[i]);
}
return;
}
static void FreeFile(char **file)
{
char **tmp = file;
while (*tmp != NULL) {
free(*tmp);
*tmp = NULL;
++tmp;
}
free(file);
}
static status_t WriteFile(char *path, uint32 pathLen, char **lines)
{
if (pathLen == 0) {
write_runlog(ERROR, "path(%s) len is zero.\n", path);
return CM_ERROR;
}
int fd;
canonicalize_path(path);
FILE *outFile = fopen(path, "w");
if (outFile == NULL) {
write_runlog(ERROR, "cm_ctl: could not open file \"%s\" for writing: %s.\n", path, gs_strerror(errno));
return CM_ERROR;
}
fd = fileno(outFile);
if ((fd >= 0) && (fchmod(fd, S_IRUSR | S_IWUSR) == -1)) {
write_runlog(ERROR, "could not set permissions of file \"%s\".\n", path);
}
rewind(outFile);
char **line = lines;
while (*line != NULL) {
if (fputs(*line, outFile) < 0) {
write_runlog(ERROR, "cm_ctl: could not write file \"%s\": %s.\n", path, gs_strerror(errno));
(void)fclose(outFile);
return CM_ERROR;
}
++line;
}
if (fsync(fileno(outFile)) != 0) {
(void)fclose(outFile);
write_runlog(ERROR, "could not fsync file \"%s\": %s.\n", path, gs_strerror(errno));
return CM_ERROR;
}
if (fclose(outFile) != 0) {
write_runlog(ERROR, "could not write file \"%s\": %s.\n", path, gs_strerror(errno));
return CM_ERROR;
}
return CM_SUCCESS;
}
static char **ReadAndBackupConfigFile(const char *readFile, char *writeFile, uint32 len)
{
status_t ret;
char **configLines = CmReadfile(readFile);
if (configLines == NULL) {
write_runlog(ERROR, "read conf file failed: %s.\n", gs_strerror(errno));
return NULL;
}
ret = WriteFile(writeFile, len, configLines);
if (ret != CM_SUCCESS) {
write_runlog(ERROR, "could not write file \"%s\": %s.\n", writeFile, gs_strerror(errno));
freefile(configLines);
return NULL;
}
return configLines;
}
void GenerateNewLine(char *oldLine, char *newLine, const char *value, const size_t valueOff, const size_t valueLen)
{
char *oldLinePtr = oldLine;
char *newLinePtr = newLine;
errno_t rc;
size_t newValueLen = (size_t)strlen(value);
rc = strncat_s(newLinePtr, MAX_PARAM_VALUE_LEN, oldLinePtr, valueOff);
securec_check_errno(rc, (void)rc);
rc = strncat_s(newLinePtr, MAX_PARAM_VALUE_LEN, value, newValueLen);
securec_check_errno(rc, (void)rc);
oldLinePtr += (valueOff + valueLen);
size_t lastLen = (size_t)strlen(oldLinePtr);
rc = strncat_s(newLinePtr, MAX_PARAM_VALUE_LEN, oldLinePtr, lastLen);
securec_check_errno(rc, (void)rc);
}
static status_t SetParameter(const GucOption *gucCtx, char **optLines)
{
errno_t rc;
size_t lineLen;
int linesIndex;
size_t optValueOff = 0;
size_t optValueLen = 0;
char newConfLine[MAX_PARAM_VALUE_LEN] = { 0 };
if (gucCtx->parameter == NULL) {
return CM_ERROR;
}
linesIndex = GetLinesIndex(optLines, gucCtx->parameter, optValueOff, optValueLen);
if (linesIndex == -1) {
return CM_ERROR;
}
lineLen = strlen(optLines[linesIndex]);
if (gucCtx->value != NULL) {
GenerateNewLine(optLines[linesIndex], newConfLine, gucCtx->value, optValueOff, optValueLen);
} else {
if (IsLineCommented(optLines[linesIndex])) {
rc = strncpy_s(newConfLine, MAX_PARAM_VALUE_LEN,
optLines[linesIndex], (size_t)Min(lineLen, (MAX_PARAM_VALUE_LEN - 1)));
securec_check_errno(rc, (void)rc);
} else {
rc = snprintf_s(newConfLine, MAX_PARAM_VALUE_LEN,
(MAX_PARAM_VALUE_LEN - 1), "#%s", optLines[linesIndex]);
securec_check_intval(rc, (void)rc);
}
}
free(optLines[linesIndex]);
optLines[linesIndex] = NULL;
optLines[linesIndex] = strdup(newConfLine);
return CM_SUCCESS;
}
static status_t ExeGucParameterValueWrite(char **optLines)
{
errno_t rc;
status_t ret;
char newTempFile[CM_PATH_LENGTH + CM_PATH_LENGTH] = { 0 };
rc = snprintf_s(newTempFile, (CM_PATH_LENGTH + CM_PATH_LENGTH), (CM_PATH_LENGTH + CM_PATH_LENGTH - 1),
"%s_bak", g_tmpFile);
securec_check_intval(rc, (void)rc);
ret = WriteFile(g_tmpFile, CM_PATH_LENGTH, optLines);
if (ret != CM_SUCCESS) {
write_runlog(ERROR, "write file %s failed, errmsg: %s.\n", g_tmpFile, gs_strerror(errno));
return CM_ERROR;
}
char **newLines = CmReadfile(g_tmpFile);
if (newLines == NULL || *newLines == NULL) {
write_runlog(ERROR, "read file \"%s\" failed: %s.\n", g_tmpFile, gs_strerror(errno));
return CM_ERROR;
}
ret = WriteFile(newTempFile, (CM_PATH_LENGTH + CM_PATH_LENGTH), newLines);
freefile(newLines);
if (ret != CM_SUCCESS) {
write_runlog(ERROR, "could not write file \"%s\": %s.\n", newTempFile, gs_strerror(errno));
return CM_ERROR;
}
if (rename(newTempFile, g_confFile) != 0) {
write_runlog(ERROR, "err while move file (%s to %s):%s.\n", newTempFile, g_confFile, gs_strerror(errno));
(void)unlink(newTempFile);
return CM_ERROR;
}
FILE *fp = fopen(g_confFile, "r");
if (fp == NULL) {
write_runlog(ERROR, "could not open file \"%s\", errmsg: %s.\n", g_confFile, gs_strerror(errno));
return CM_ERROR;
}
if (fsync(fileno(fp)) != 0) {
write_runlog(ERROR, "could not fsync file \"%s\": %s.\n", g_confFile, gs_strerror(errno));
(void)fclose(fp);
return CM_ERROR;
}
(void)fclose(fp);
return CM_SUCCESS;
}
static status_t ExeGucConfigReload()
{
long pid;
FILE* pidFd = fopen(g_pidFile, "r");
if (pidFd == NULL) {
if (errno != ENOENT) {
write_runlog(ERROR, "cm_ctl: could not open PID file \"%s\":%s.\n", g_pidFile, gs_strerror(errno));
return CM_ERROR;
}
pid = 0;
} else {
if (fscanf_s(pidFd, "%ld", &pid) != 1) {
write_runlog(ERROR, "cm_ctl: invalid data in PID file \"%s\".\n", g_pidFile);
(void)fclose(pidFd);
return CM_ERROR;
}
(void)fclose(pidFd);
}
if (pid == 0) {
write_runlog(ERROR, "cm_ctl: PID file \"%s\" does not exist.\n", g_pidFile);
write_runlog(ERROR, "Is cma or cms running\n");
return CM_ERROR;
} else if (pid < 0) {
pid = -pid;
write_runlog(ERROR, "cm_ctl: cannot reload, single-user server is running (PID: %ld).\n", pid);
write_runlog(ERROR, "Please terminate the single-user server and try again.\n");
return CM_ERROR;
}
if (kill((pid_t)pid, SIGHUP) != 0) {
write_runlog(ERROR, "cm_ctl: could not send reload signal(SIGHUP) PID:%ld %s.\n", pid, gs_strerror(errno));
return CM_ERROR;
}
return CM_SUCCESS;
}
static status_t ExeGucConfigSet(const GucOption *gucCtx)
{
status_t result;
struct stat statBuf = { 0 };
struct stat tempBuf = { 0 };
char **configLines = NULL;
if (CheckConfigFileStatus(statBuf, tempBuf) != CM_SUCCESS) {
return CM_ERROR;
}
if (statBuf.st_size == 0 && tempBuf.st_size != 0) {
write_runlog(ERROR, "The last signal is now, waiting....\n");
return CM_ERROR;
}
if (lstat(g_confFile, &statBuf) != 0) {
configLines = ReadAndBackupConfigFile(g_tmpFile, g_confFile, CM_PATH_LENGTH);
} else {
configLines = ReadAndBackupConfigFile(g_confFile, g_tmpFile, CM_PATH_LENGTH);
}
if (configLines == NULL) {
return CM_ERROR;
}
if (SetParameter(gucCtx, configLines) != CM_SUCCESS) {
write_runlog(ERROR, "can't find the parameter in conf.\n");
FreeFile(configLines);
return CM_ERROR;
}
result = ExeGucParameterValueWrite(configLines);
FreeFile(configLines);
return result;
}
static status_t ExeGucConfigList()
{
struct stat statBuf = { 0 };
struct stat tempBuf = { 0 };
if (CheckConfigFileStatus(statBuf, tempBuf) != CM_SUCCESS) {
return CM_ERROR;
}
if (statBuf.st_size == 0 && tempBuf.st_size != 0) {
write_runlog(ERROR, "The last signal is now, waiting....\n");
return CM_ERROR;
}
char **configLines = CmReadfile(g_confFile);
if (configLines == NULL || *configLines == NULL) {
write_runlog(ERROR, "read conf file failed: %s.\n", gs_strerror(errno));
return CM_ERROR;
}
PrintValueAndParameter(configLines);
FreeFile(configLines);
return CM_SUCCESS;
}
status_t ExeGucCommand(const GucOption *gucCtx)
{
status_t result;
switch (gucCtx->gucCommand) {
case SET_CONF_COMMAND:
result = ExeGucConfigSet(gucCtx);
break;
case RELOAD_CONF_COMMAND:
result = ExeGucConfigReload();
break;
case LIST_CONF_COMMAND:
result = ExeGucConfigList();
break;
default:
result = CM_ERROR;
break;
}
return result;
}
static uint32 GetNodeIndex(uint32 nodeId)
{
for (uint32 i = 0; i < g_node_num; ++i) {
if (g_node[i].node == nodeId) {
return i;
}
}
return 0;
}
static status_t ListRemoteConf(const char *actualCmd, uint32 nodeId)
{
char buf[MAX_PATH_LEN] = {0};
FILE *fp = popen(actualCmd, "r");
if (fp == NULL) {
write_runlog(DEBUG1, "execute cmd(%s) failed.\n", actualCmd);
return CM_ERROR;
}
if (fgets(buf, sizeof(buf), fp) != NULL) {
(void)printf(_("%s"), buf);
} else {
write_runlog(LOG, "execute cmd (%s) failed, or conf of node(%u) is empty.\n", actualCmd, nodeId);
(void)pclose(fp);
return CM_ERROR;
}
while (fgets(buf, sizeof(buf), fp) != NULL) {
(void)printf(_("%s"), buf);
}
(void)pclose(fp);
return CM_SUCCESS;
}
static status_t ListRemoteConfMain(staticNodeConfig *node, const char *cmd)
{
int ret;
char actualCmd[MAX_PATH_LEN] = {0};
for (uint32 i = 0; i < node->sshCount; ++i) {
if (mpp_env_separate_file[0] == '\0') {
ret = snprintf_s(actualCmd, MAX_PATH_LEN, MAX_PATH_LEN - 1,
"pssh %s -H %s \"%s\" | sed '1d;$d'",
PSSH_TIMEOUT_OPTION, node->sshChannel[i], cmd);
securec_check_intval(ret, (void)ret);
} else {
ret = snprintf_s(actualCmd, MAX_PATH_LEN, MAX_PATH_LEN - 1,
"pssh %s -H %s \"source %s;%s \" | sed '1d;$d'",
PSSH_TIMEOUT_OPTION, node->sshChannel[i], mpp_env_separate_file, cmd);
securec_check_intval(ret, (void)ret);
}
if (ListRemoteConf(actualCmd, node->node) == CM_SUCCESS) {
write_runlog(DEBUG1, "execute remote cmd(%s) success.\n", actualCmd);
return CM_SUCCESS;
}
}
return CM_ERROR;
}
status_t ProcessInLocalInstanceExec(const GucOption *gucCtx)
{
errno_t rc;
char cmDir[CM_PATH_LENGTH] = { 0 };
char instanceDir[CM_PATH_LENGTH] = { 0 };
rc = memcpy_s(cmDir, sizeof(cmDir), g_currentNode->cmDataPath, sizeof(cmDir));
securec_check_errno(rc, (void)rc);
if (cmDir[0] == '\0') {
write_runlog(ERROR, "Failed to get cm base data path from static config file.");
return CM_ERROR;
}
if (gucCtx->nodeType == NODE_TYPE_AGENT) {
rc = snprintf_s(instanceDir, sizeof(instanceDir), sizeof(instanceDir) - 1, "%s/cm_agent", cmDir);
securec_check_intval(rc, (void)rc);
} else {
if (g_currentNode->cmServerLevel != 1) {
write_runlog(LOG, "There is no cmserver instance on local node.");
return CM_ERROR;
}
rc = snprintf_s(instanceDir, sizeof(instanceDir), sizeof(instanceDir) - 1, "%s/cm_server", cmDir);
securec_check_intval(rc, (void)rc);
}
GetInstanceConfigfile(gucCtx->nodeType, instanceDir);
if (ExeGucCommand(gucCtx) != CM_SUCCESS) {
return CM_ERROR;
}
return CM_SUCCESS;
}
status_t ProcessInLocalInstance(const CtlOption *ctx)
{
if (CheckGucOption(ctx->guc) != CM_SUCCESS) {
return CM_ERROR;
}
if (ctx->guc.gucCommand == SET_CONF_COMMAND && CheckGucOptionValidate(ctx->guc) != CM_SUCCESS) {
DoAdvice();
return CM_ERROR;
}
return ProcessInLocalInstanceExec(&ctx->guc);
}
static status_t ProcessInRemoteInstance(const CtlOption *ctx)
{
char remoteCmd[MAX_COMMAND_LEN] = {0};
GetRemoteGucCommand(ctx, remoteCmd, sizeof(remoteCmd));
if (ctx->guc.gucCommand == LIST_CONF_COMMAND) {
return ListRemoteConfMain(&g_node[GetNodeIndex(ctx->comm.nodeId)], remoteCmd);
}
if (ssh_exec(&g_node[GetNodeIndex(ctx->comm.nodeId)], remoteCmd) != 0) {
write_runlog(DEBUG1, "cm_ctl fail to execute command %s, errno=%d.\n", remoteCmd, errno);
return CM_ERROR;
}
return CM_SUCCESS;
}
static status_t ProcessInAllNodesInstance(CtlOption *ctx)
{
status_t result = CM_SUCCESS;
for (uint32 i = 0; i < g_node_num; ++i) {
if (g_node[i].cmServerLevel != 1 && ctx->guc.nodeType == NODE_TYPE_SERVER) {
continue;
}
ctx->comm.nodeId = g_node[i].node;
if (ctx->comm.nodeId == g_currentNode->node) {
result = ProcessInLocalInstance(ctx);
} else {
result = ProcessInRemoteInstance(ctx);
}
}
return result;
}
status_t ProcessClusterGucOption(CtlOption *ctx)
{
if (ctx->comm.nodeId == 0) {
return ProcessInAllNodesInstance(ctx);
}
if (ctx->comm.nodeId != g_currentNode->node) {
return ProcessInRemoteInstance(ctx);
}
status_t res = ProcessInLocalInstance(ctx);
if (res == CM_ERROR) {
write_runlog(DEBUG1, "cm_ctl fail to execute in local.\n");
}
return res;
}
static status_t CheckGucOption(const GucOption &gucCtx)
{
if (!gucCtx.needDoGuc) {
write_runlog(LOG, "command wrong, need add \"--param\".\n");
return CM_ERROR;
}
if (gucCtx.nodeType == NODE_TYPE_UNDEF) {
write_runlog(LOG, "command wrong, need add \"--agent\" or \"--server\".\n");
return CM_ERROR;
}
return CM_SUCCESS;
}
int DoGuc(CtlOption *ctx)
{
status_t res = ProcessClusterGucOption(ctx);
PrintResults(res == CM_SUCCESS, ctx);
return (int)res;
}
static void MemsetPassword(char **password)
{
if (password == NULL || (*password) == NULL) {
return;
}
size_t len = strlen(*password);
const int32 tryTimes = 3;
for (int32 i = 0; i < tryTimes; ++i) {
errno_t rc = memset_s((*password), len, 0, len);
securec_check_errno(rc, (void)rc);
}
FREE_AND_RESET((*password));
}
static const char *GetModeString(const KeyMode &mode)
{
switch (mode) {
case SERVER_MODE:
return "server";
case CLIENT_MODE:
return "client";
default:
break;
}
return "";
}
static inline bool IsPathPermissionRight(const char *path)
{
bool isR = (access(path, R_OK) == 0);
bool isW = (access(path, W_OK) == 0);
bool isX = (access(path, X_OK) == 0);
return (isR && isW && isX);
}
static char *CmSimplePrompt(const char *tipsStr, uint32 maxlen, bool echo)
{
char *destBuff = (char *)malloc((size_t)maxlen + 1);
if (destBuff == NULL) {
return NULL;
}
struct termios oldTms, t;
FILE *termIn = fopen("/dev/tty", "r");
FILE *termOut = fopen("/dev/tty", "w");
if ((termIn == NULL) || (termOut == NULL)) {
if (termIn != NULL) {
(void)fclose(termIn);
}
if (termOut != NULL) {
(void)fclose(termOut);
}
termIn = stdin;
termOut = stderr;
}
if (!echo) {
(void)tcgetattr(fileno(termIn), &t);
oldTms = t;
t.c_lflag &= ~ECHO;
(void)tcsetattr(fileno(termIn), TCSAFLUSH, &t);
}
if (tipsStr != NULL) {
(void)fputs(_(tipsStr), termOut);
(void)fflush(termOut);
}
if (fgets(destBuff, (int)maxlen + 1, termIn) == NULL) {
destBuff[0] = '\0';
}
size_t destBuffLen = strlen(destBuff);
if (destBuffLen > 0 && destBuff[destBuffLen - 1] != '\n') {
char buf[128];
size_t bufLen;
do {
if (fgets(buf, sizeof(buf), termIn) == NULL) {
break;
}
bufLen = strlen(buf);
} while (bufLen > 0 && buf[bufLen - 1] != '\n');
}
if (destBuffLen > 0 && destBuff[destBuffLen - 1] == '\n') {
destBuff[destBuffLen - 1] = '\0';
}
if (!echo) {
(void)tcsetattr(fileno(termIn), TCSAFLUSH, &oldTms);
(void)fputs("\n", termOut);
(void)fflush(termOut);
}
if (termIn != stdin) {
(void)fclose(termIn);
(void)fclose(termOut);
}
return destBuff;
}
int DoEncrypt(const CtlOption *ctx)
{
int ret;
write_runlog(DEBUG1, "exec \"cm_ctl encrypt -M %s -D %s\".\n", GetModeString(ctx->guc.keyMod), ctx->comm.dataPath);
if (!IsPathPermissionRight(ctx->comm.dataPath)) {
write_runlog(LOG, "-D path not exist or permission denied.\n");
return 1;
}
write_runlog(DEBUG1, "enter password.\n");
char *password = CmSimplePrompt("please enter the password:", KEY_LEN + 1, false);
if (!CheckInputPassword(password)) {
write_runlog(LOG, "The input key must be 8~15 bytes and contain at least three kinds of characters!\n");
MemsetPassword(&password);
return 1;
}
write_runlog(DEBUG1, "enter password again.\n");
char *passwordAgain = CmSimplePrompt("please enter the password again:", KEY_LEN + 1, false);
if (passwordAgain == NULL || strcmp(password, passwordAgain) != 0) {
write_runlog(LOG, "two passwords do not match!\n");
MemsetPassword(&passwordAgain);
MemsetPassword(&password);
return 1;
}
write_runlog(DEBUG1, "clear secondary input.\n");
MemsetPassword(&passwordAgain);
write_runlog(DEBUG1, "begin to generate cipher file.\n");
ret = GenCipherRandFiles(ctx->guc.keyMod, password, ctx->comm.dataPath);
write_runlog(DEBUG1, "clear password.\n");
MemsetPassword(&password);
if (ret == 0) {
write_runlog(LOG, "encrypt success.\n");
return 0;
}
write_runlog(LOG, "encrypt fail.\n");
return 1;
}