* Copyright (c) 2022 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.
* -------------------------------------------------------------------------
*
* cm_json_config.cpp
*
*
* IDENTIFICATION
* src/cm_common/cm_json_config.cpp
*
* -------------------------------------------------------------------------
*/
#include <ctype.h>
#include "cm_defs.h"
#include "elog.h"
#include "cm_text.h"
#include "cm_json_config.h"
#define CM_SET_READ_JSON_ERR(errPtr, err) \
do { \
if ((errPtr) != NULL) { \
(*(errPtr)) = (err); \
} \
} while (0)
typedef void (*ParseCusRes)(const cJSON *resJson, OneCusResConfJson *resConf);
static void ParseAppResConfJson(const cJSON *resJson, OneCusResConfJson *resConf);
static void ParseDnResConfJson(const cJSON *resJson, OneCusResConfJson *resConf);
static void ParseVipResConfJson(const cJSON *resJson, OneCusResConfJson *resConf);
static void EmptyCmJsonWriteLog(int logLevel, const char *format, ...)
{
return;
}
typedef struct ParseCusResMapSt {
const char *resTypeName;
CusResType resType;
ParseCusRes parseFunc;
} ParseCusResMap;
CmConfJson *g_confJson = NULL;
static CmJsonLogOutput CmJsonWriteLog = EmptyCmJsonWriteLog;
static ParseCusResMap g_cusResMap[] = {
{"APP", CUSTOM_RESOURCE_APP, ParseAppResConfJson},
{"DN", CUSTOM_RESOURCE_DN, ParseDnResConfJson},
{"VIP", CUSTOM_RESOURCE_VIP, ParseVipResConfJson},
};
static void *CmJsonMalloc(size_t size)
{
if (size == 0) {
CmJsonWriteLog(FATAL, "[CmJsonMalloc] malloc 0.\n");
exit(1);
}
void *result = malloc(size);
if (result == NULL) {
CmJsonWriteLog(FATAL, "[CmJsonMalloc] malloc failed, out of memory.\n");
exit(1);
}
errno_t rc = memset_s(result, size, 0, size);
securec_check_errno(rc, (void)rc);
return result;
}
static void CmJsonCheckForSecurity(const char *input)
{
const char *dangerCharList[] = {"|", ";", "&", "$", "<", ">", "`", "\\", "'", "\"", "{", "}",
"(", ")", "[", "]", "~", "*", "?", "!", "\n", NULL};
for (int i = 0; dangerCharList[i] != NULL; i++) {
if (strstr(input, dangerCharList[i]) != NULL) {
CmJsonWriteLog(FATAL, "invalid token %s in input:\"%s\".\n", dangerCharList[i], input);
exit(1);
}
}
}
static void CmJsonCheckScriptPath(const char *input)
{
if (!is_absolute_path(input)) {
CmJsonWriteLog(FATAL, "invalid script path \"%s\", must be an absolute path.\n", input);
exit(1);
}
for (const char *p = input; *p != '\0'; ++p) {
if (isspace((unsigned char)*p) != 0) {
CmJsonWriteLog(FATAL, "invalid whitespace in script path:\"%s\".\n", input);
exit(1);
}
}
}
static int GetValueIntFromJson(int *infoValue, const cJSON *object, const char *infoKey)
{
cJSON *objValue = cJSON_GetObjectItem(object, infoKey);
if (!cJSON_IsNumber(objValue)) {
CmJsonWriteLog(WARNING, "[ReadConfJson] (%s) object is not number or not exit.\n", infoKey);
return 1;
}
if (infoValue != NULL) {
*infoValue = objValue->valueint;
}
return 0;
}
static int GetValueStrFromJson(char *valueStr, uint32 valueLen, const cJSON *object, const char *infoKey)
{
cJSON *objValue = cJSON_GetObjectItem(object, infoKey);
if (!cJSON_IsString(objValue)) {
CmJsonWriteLog(WARNING, "[ReadConfJson] (%s) object is not string or not exit.\n", infoKey);
return 1;
}
if (objValue->valuestring[0] == '\0') {
CmJsonWriteLog(WARNING, "[ReadConfJson] (%s) object is an empty string.\n", infoKey);
return 1;
}
if (valueStr != NULL) {
errno_t rc = strcpy_s(valueStr, valueLen, objValue->valuestring);
securec_check_errno(rc, (void)rc);
CmJsonCheckForSecurity(valueStr);
}
return 0;
}
static void ParseOneCusResInstConfJson(const cJSON *instJson, CusResInstConf *instConf)
{
const int defValue = -1;
if (GetValueIntFromJson(&instConf->nodeId, instJson, "node_id") != 0) {
instConf->nodeId = defValue;
}
if (GetValueIntFromJson(&instConf->resInstId, instJson, "res_instance_id") != 0) {
instConf->resInstId = defValue;
}
if (GetValueStrFromJson(instConf->resArgs, CM_JSON_STR_LEN, instJson, "res_args") != 0) {
errno_t rc = memset_s(instConf->resArgs, CM_JSON_STR_LEN, 0, CM_JSON_STR_LEN);
securec_check_errno(rc, (void)rc);
}
}
static void ParseAllCusResInstConfJson(const cJSON *resJson, CusResConfJson *resConf)
{
cJSON *instArr = cJSON_GetObjectItem(resJson, "instances");
if (!cJSON_IsArray(instArr)) {
if (instArr != NULL) {
CmJsonWriteLog(WARNING, "[ReadConfJson] \"instances\" obj is not an array, can't parse continue.\n");
}
return;
}
int arrLen = cJSON_GetArraySize(instArr);
if (arrLen <= 0) {
CmJsonWriteLog(WARNING, "[ReadConfJson] inst array len invalid, arrLen=%d, can't parse continue.\n", arrLen);
return;
}
resConf->instance.count = (uint32)arrLen;
resConf->instance.conf = (CusResInstConf*)CmJsonMalloc((uint32)arrLen * sizeof(CusResInstConf));
for (int i = 0; i < arrLen; ++i) {
cJSON *resItem = cJSON_GetArrayItem(instArr, i);
if (resItem != NULL) {
ParseOneCusResInstConfJson(resItem, &resConf->instance.conf[i]);
}
}
}
static void ParseAppDnResConfJson(const cJSON *resJson, CusResConfJson *resConf)
{
errno_t rc;
const int defValue = -1;
if (GetValueStrFromJson(resConf->resName, CM_JSON_STR_LEN, resJson, "name") != 0) {
rc = memset_s(resConf->resName, CM_JSON_STR_LEN, 0, CM_JSON_STR_LEN);
securec_check_errno(rc, (void)rc);
}
if (GetValueStrFromJson(resConf->resScript, CM_JSON_STR_LEN, resJson, "script") != 0) {
rc = memset_s(resConf->resScript, CM_JSON_STR_LEN, 0, CM_JSON_STR_LEN);
securec_check_errno(rc, (void)rc);
} else {
CmJsonCheckScriptPath(resConf->resScript);
}
if (GetValueIntFromJson(&resConf->checkInterval, resJson, "check_interval") != 0) {
resConf->checkInterval = defValue;
}
if (GetValueIntFromJson(&resConf->timeOut, resJson, "time_out") != 0) {
resConf->timeOut = defValue;
}
if (GetValueIntFromJson(&resConf->restartDelay, resJson, "restart_delay") != 0) {
resConf->restartDelay = defValue;
}
if (GetValueIntFromJson(&resConf->restartPeriod, resJson, "restart_period") != 0) {
resConf->restartPeriod = defValue;
}
if (GetValueIntFromJson(&resConf->restartTimes, resJson, "restart_times") != 0) {
resConf->restartTimes = defValue;
}
if (GetValueIntFromJson(&resConf->abnormalTimeout, resJson, "abnormal_timeout") != 0) {
resConf->abnormalTimeout = defValue;
}
}
static void ParseAppResConfJson(const cJSON *resJson, OneCusResConfJson *resConf)
{
ParseAppDnResConfJson(resJson, &resConf->appResConf);
ParseAllCusResInstConfJson(resJson, &resConf->appResConf);
}
static void ParseDnResConfJson(const cJSON *resJson, OneCusResConfJson *resConf)
{
ParseAppDnResConfJson(resJson, &resConf->dnResConf);
ParseAllCusResInstConfJson(resJson, &resConf->dnResConf);
}
int FetchStrFromText(const char *textStr, char *result, uint32 len, char beginPoint)
{
bool8 isFetch = CM_FALSE;
uint32 point = 0;
for (uint32 i = 0; textStr[i] != '\0'; ++i) {
if (!isFetch) {
if (textStr[i] == beginPoint) {
isFetch = CM_TRUE;
}
continue;
}
if (textStr[i] == SEPARATOR_CHAR) {
break;
}
if (point >= len) {
return -1;
}
result[point] = textStr[i];
++point;
}
CmTrimStr(result);
if (result[0] == '\0') {
return -1;
}
return 0;
}
int GetValueStrFromText(char *result, uint32 resultLen, const char *textStr, const char *expectValue)
{
const char *point = strstr(textStr, expectValue);
if (point == NULL) {
write_runlog(ERROR, "Failed to get value str from text, when textStr=[%s], expectValue=[%s].\n",
textStr, expectValue);
return -1;
}
if (FetchStrFromText(point, result, resultLen, '=') != 0) {
write_runlog(ERROR, "Failed to fetch text from string, when textStr=[%s], expectValue=[%s].\n",
textStr, expectValue);
return -1;
}
return 0;
}
static void ParseOneBaseIp(const cJSON *ipJson, BaseIpListConf *ipConf)
{
if (GetValueIntFromJson(&ipConf->instId, ipJson, "res_instance_id") != 0) {
ipConf->instId = 0;
}
char baseIp[CM_JSON_STR_LEN] = {0};
if (GetValueStrFromJson(baseIp, CM_JSON_STR_LEN, ipJson, "inst_attr") != 0) {
return;
}
if (GetValueStrFromText(ipConf->baseIp, CM_JSON_STR_LEN, baseIp, "base_ip") != 0) {
errno_t rc = memset_s(ipConf->baseIp, CM_JSON_STR_LEN, 0, CM_JSON_STR_LEN);
securec_check_errno(rc, (void)rc);
}
}
static void ParseAllBaseIp(const cJSON *resJson, VipCusResConfJson *resConf)
{
cJSON *baseIpArray = cJSON_GetObjectItem(resJson, "instances");
if (!cJSON_IsArray(baseIpArray)) {
if (baseIpArray != NULL) {
CmJsonWriteLog(WARNING, "[ReadConfJson] \"instances\" obj is not an array, can't parse continue.\n");
}
return;
}
int arrLen = cJSON_GetArraySize(baseIpArray);
if (arrLen <= 0) {
CmJsonWriteLog(WARNING, "[ReadConfJson] baseIp array len invalid, arrLen=%d, can't parse continue.\n", arrLen);
return;
}
resConf->baseIpList.count = (uint32)arrLen;
resConf->baseIpList.conf = (BaseIpListConf*)CmJsonMalloc((uint32)arrLen * sizeof(BaseIpListConf));
for (int i = 0; i < arrLen; ++i) {
cJSON *resItem = cJSON_GetArrayItem(baseIpArray, i);
if (resItem != NULL) {
ParseOneBaseIp(resItem, &resConf->baseIpList.conf[i]);
}
}
}
static void ParseVipResConfJson(const cJSON *resJson, OneCusResConfJson *resConf)
{
errno_t rc;
if (GetValueStrFromJson(resConf->vipResConf.resName, CM_JSON_STR_LEN, resJson, "name") != 0) {
rc = memset_s(resConf->vipResConf.resName, CM_JSON_STR_LEN, 0, CM_JSON_STR_LEN);
securec_check_errno(rc, (void)rc);
}
if (GetValueStrFromJson(resConf->vipResConf.floatIp, CM_JSON_STR_LEN, resJson, "float_ip") != 0) {
rc = memset_s(resConf->vipResConf.floatIp, CM_JSON_STR_LEN, 0, CM_JSON_STR_LEN);
securec_check_errno(rc, (void)rc);
}
if (GetValueStrFromJson(resConf->vipResConf.cmd, CM_JSON_STR_LEN, resJson, "cmd") != 0) {
rc = memset_s(resConf->vipResConf.cmd, CM_JSON_STR_LEN, 0, CM_JSON_STR_LEN);
securec_check_errno(rc, (void)rc);
}
if (GetValueStrFromJson(resConf->vipResConf.netMask, CM_JSON_STR_LEN, resJson, "netMask") != 0) {
rc = memset_s(resConf->vipResConf.netMask, CM_JSON_STR_LEN, 0, CM_JSON_STR_LEN);
securec_check_errno(rc, (void)rc);
}
ParseAllBaseIp(resJson, &resConf->vipResConf);
}
static void ParseOneCusResConfJson(const cJSON *resItem, OneCusResConfJson *resConf)
{
char resType[CM_JSON_STR_LEN] = {0};
if (GetValueStrFromJson(resType, CM_JSON_STR_LEN, resItem, "resources_type") != 0) {
CmJsonWriteLog(ERROR, "[ReadConfJson] unknown resources_type, can't parse current resource continue.\n");
return;
}
size_t arrLen = sizeof(g_cusResMap) / sizeof(g_cusResMap[0]);
for (size_t i = 0; i < arrLen; ++i) {
if (strcmp(resType, g_cusResMap[i].resTypeName) == 0) {
resConf->resType = g_cusResMap[i].resType;
g_cusResMap[i].parseFunc(resItem, resConf);
}
}
}
static void ParseAllCusResConfJson(const cJSON *resArr, CmConfJson *cmConf)
{
int arrLen = cJSON_GetArraySize(resArr);
if (arrLen <= 0) {
CmJsonWriteLog(ERROR, "[ReadConfJson] resource array size(%d) is invalid, can't parse continue.\n", arrLen);
return;
}
cmConf->resource.count = (uint32)arrLen;
cmConf->resource.conf = (OneCusResConfJson*)CmJsonMalloc((uint32)arrLen * sizeof(OneCusResConfJson));
for (int i = 0; i < arrLen; ++i) {
cJSON *resItem = cJSON_GetArrayItem(resArr, i);
if (!cJSON_IsObject(resItem)) {
CmJsonWriteLog(WARNING, "[ReadConfJson] index(%d) of res array is not an object.\n", i);
continue;
}
ParseOneCusResConfJson(resItem, &cmConf->resource.conf[i]);
}
}
void ParseRootJson(const cJSON *root, CmConfJson *cmConf)
{
if (cmConf == NULL) {
CmJsonWriteLog(WARNING, "[ReadConfJson] cmConf is null, can't do parse.\n");
return;
}
if (root == NULL) {
CmJsonWriteLog(WARNING, "[ReadConfJson] conf json is null, can't do parse.\n");
return;
}
cJSON *resObj = cJSON_GetObjectItem(root, "resources");
if (cJSON_IsArray(resObj)) {
ParseAllCusResConfJson(resObj, cmConf);
} else {
CmJsonWriteLog(WARNING, "[ReadConfJson] \"resources\" obj is not an array.\n");
}
}
int ReadConfJsonFile(const char *jsonFile)
{
int readJsonErr = 0;
cJSON *root = ReadJsonFile(jsonFile, &readJsonErr);
if (readJsonErr != 0) {
CmJsonWriteLog(LOG, "[ReadConfJson] read conf json:\"%s\" failed, err=%d.\n", jsonFile, readJsonErr);
return readJsonErr;
}
if (g_confJson == NULL) {
g_confJson = (CmConfJson*)CmJsonMalloc(sizeof(CmConfJson));
}
ParseRootJson(root, g_confJson);
cJSON_Delete(root);
return 0;
}
void SetReadJsonConfWriteLog(CmJsonLogOutput logFunc)
{
if (logFunc != NULL) {
CmJsonWriteLog = logFunc;
}
}
bool IsConfJsonEmpty()
{
return (g_confJson == NULL);
}
cJSON *ReadJsonFile(const char *jsonPath, int *err)
{
FILE *fd = fopen(jsonPath, "r");
if (fd == NULL) {
if (errno == ENOENT) {
CM_SET_READ_JSON_ERR(err, CM_JSON_NOT_EXIST);
return NULL;
}
CM_SET_READ_JSON_ERR(err, CM_JSON_OPEN_ERROR);
return NULL;
}
long size = (fseek(fd, 0, SEEK_END) == 0) ? ftell(fd) : 0;
if (size <= 0) {
CM_SET_READ_JSON_ERR(err, CM_JSON_GET_LEN_ERROR);
(void)fclose(fd);
return NULL;
}
char *jsonData = (char*)malloc((size_t)(size + 1));
if (jsonData == NULL) {
CM_SET_READ_JSON_ERR(err, CM_JSON_OUT_OF_MEMORY);
(void)fclose(fd);
return NULL;
}
errno_t rc = memset_s(jsonData, (size_t)(size + 1), 0, (size_t)(size + 1));
securec_check_errno(rc, (void)rc);
if ((fseek(fd, 0, SEEK_SET) != 0) || (fread(jsonData, 1, (size_t)size, fd) == 0)) {
CM_SET_READ_JSON_ERR(err, CM_JSON_READ_ERROR);
FREE_AND_RESET(jsonData);
(void)fclose(fd);
return NULL;
}
cJSON *root = cJSON_Parse(jsonData);
CM_SET_READ_JSON_ERR(err, 0);
FREE_AND_RESET(jsonData);
(void)fclose(fd);
return root;
}