/*
 * 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.
 * -------------------------------------------------------------------------
 *
 * cma_status_check.cpp
 *    cma process cms messages functions
 *
 * IDENTIFICATION
 *    src/cm_agent/cma_status_check.cpp
 *
 * -------------------------------------------------------------------------
 */
#include <mntent.h>
#include <errno.h>
#include <strings.h>
#include <sys/stat.h>
#include <sys/vfs.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include "cma_connect.h"
#include "cma_global_params.h"
#include "cma_common.h"
#include "cm_text.h"
#include "cma_client.h"
#include "cma_instance_management.h"
#include "cma_instance_management_res.h"
#include "cma_process_messages.h"
#include "cma_connect.h"
#include "cma_instance_check.h"
#include "cm_util.h"
#ifdef ENABLE_MULTIPLE_NODES
#include "cma_coordinator.h"
#endif
#ifdef ENABLE_XALARMD
#ifdef __cplusplus
extern "C" {
#endif
#include <xalarm/register_xalarm.h>
#ifdef __cplusplus
}
#endif
#include "cjson/cJSON.h"
#include "cma_xalarm_event_compat.h"
struct alarm_register *g_xalarmEventRegister = NULL;
static const uint32 XALARM_TYPE_OCCUR = 1;
static const uint32 XALARM_TYPE_RECOVER = 2;
static const uint32 XALARM_REBOOT_EVENT_ID = 1003;
static const uint32 XALARM_REBOOT_ACK_EVENT_ID = 1004;
static const uint32 XALARM_OOM_EVENT_ID = 1005;
static const uint32 XALARM_OOM_ACK_EVENT_ID = 1006;
static const uint32 XALARM_PANIC_EVENT_ID = 1007;
static const uint32 XALARM_PANIC_ACK_EVENT_ID = 1008;
static const uint32 XALARM_KERNEL_REBOOT_EVENT_ID = 1009;
static const uint32 XALARM_KERNEL_REBOOT_ACK_EVENT_ID = 1010;
static const uint32 XALARM_UBUS_MEM_EVENT_ID = 1013;
static const uint32 XALARM_EVENT_NODE_MAP_MAX = CM_NODE_MAXNUM;
static const uint32 XALARM_EVENT_MAP_TEXT_LEN = MAX_PATH_LEN * 4;
static const uint32 XALARM_EVENT_COUNT = 5;
static const uint32 INVALID_ALARM_NODE_ID = 0;
static const int DECIMAL_BASE = 10;
static const uint32 MICROSECONDS_PER_MILLISECOND = 1000;
static const uint32 CMS_PRIMARY_SWITCH_WAIT_TIMEOUT_MS = 30000;

typedef struct XalarmNodeMapItemT {
    uint32 nodeId;
    uint32 cna;
} XalarmNodeMapItem;

static XalarmNodeMapItem g_xalarmNodeMap[XALARM_EVENT_NODE_MAP_MAX];
static uint32 g_xalarmNodeMapCount = 0;
#endif
#include "cma_status_check.h"
#include "cm_ip.h"
#include "cm_msg_version_convert.h"

/*
 * dilatation status. If the cluster in dilatation status, we query  coordinate and report status for every loop. Or,
 * only query once. If you want to use this flag, you need to restart cm_agent. The default value is true means, we need
 * to query coordinate at least once at the start time.
 */
const int ETCD_CHECK_TIMES = 3;
static const int THRESHOLD_FORMAT = 4;
static const int THRESHOLD_MAX_VALUE = 100;
static const int THRESHOLD_MIN_VALUE = 0;
static const int INFO_POS = 5;
static const uint64 SLOW_DISK_CHECK_PERIOD = 5 * 60;
static const uint64 SLOW_DISK_CHECK_THRESHOLD = SLOW_DISK_CHECK_PERIOD * 2 / 10;

static uint32 g_sys_report_interval = 1;

MemCheckInfo g_memoryCheckInfoList[] = {
    {"MemTotal", "MemTotal: %lu kB", MEM_STAT_TOTAL},
    {"MemFree", "MemFree: %lu kB", MEM_STAT_FREE},
    {"MemAvailable", "MemAvailable: %lu kB", MEM_STAT_AVAILABLE},
    {"Buffers", "Buffers: %lu kB", MEM_STAT_BUFFERS},
    {"Cached", "Cached: %lu kB", MEM_STAT_CACHED}
};

SlowIotLevelInfo g_slowIoLevelInfoList[] = {
    {SVCTM_LEVEL_SLIGHT, 0, 0},
    {SVCTM_LEVEL_MODERATE, 10, 1},
    {SVCTM_LEVEL_SERIOUS, 20, 2}
};

void etcd_status_check_and_report(void)
{
    if (g_currentNode->etcd == 0) {
        return;
    }

    cm_query_instance_status reportMsg = {0};
    (void)pthread_rwlock_wrlock(&(g_etcdReportMsg.lk_lock));
    errno_t rc = memcpy_s(&reportMsg, sizeof(cm_query_instance_status),
        &(g_etcdReportMsg.report_msg), sizeof(cm_query_instance_status));
    securec_check_errno(rc, (void)rc);
    (void)pthread_rwlock_unlock(&(g_etcdReportMsg.lk_lock));

    PushMsgToCmsSendQue((char *)&reportMsg, (uint32)sizeof(cm_query_instance_status), "etcd status");
}

static status_t GetCpuInfo(IoStat *stat, CpuInfo &cpu, const char *line)
{
    int ret;

    ret = sscanf_s(line + INFO_POS, "%lu %lu %lu %lu %lu %lu %lu %lu %lu %lu", &cpu.cpuUser, &cpu.cpuNice, &cpu.cpuSys,
        &cpu.cpuIdle, &cpu.cpuIwait, &cpu.cpuHardirq, &cpu.cpuSoftirq, &cpu.cpuSteal, &cpu.cpuGuest, &cpu.cpuGuestNice);
    if (ret == -1) {
        write_runlog(ERROR, "get cpu info fail, result is %d.\n", ret);
        return CM_ERROR;
    }
    if (stat != NULL) {
        stat->idle = cpu.cpuIdle;
        stat->uptime = cpu.cpuUser + cpu.cpuNice + cpu.cpuSys + cpu.cpuIdle + cpu.cpuIwait + cpu.cpuHardirq +
            cpu.cpuSteal + cpu.cpuSoftirq;
    }

    return CM_SUCCESS;
}

static int ReadCpuStatus(int cpu_nr, IoStat *stat, bool getTotalCpuHave)
{
    FILE* cpufp;
    char line[8192];
    CpuInfo cpu = {0};

    if ((cpufp = fopen(FILE_CPUSTAT, "re")) == NULL) {
        write_runlog(ERROR, "cannot open file: %s \n", FILE_CPUSTAT);
        return -1;
    }
    while (fgets(line, sizeof(line), cpufp) != NULL) {
        /* first line -- total cpu */
        if (!strncmp(line, "cpu ", 4) && (cpu_nr == 1 || getTotalCpuHave)) {
            /* for non smp iostat or cpu stat, get the total jiffies */
            if (GetCpuInfo(stat, cpu, line) != CM_SUCCESS) {
                (void)fclose(cpufp);
                return -1;
            }
            (void)fclose(cpufp);
            uint64 tmp = cpu.cpuUser + cpu.cpuNice + cpu.cpuSys;
            uint64 total = tmp + cpu.cpuIdle;
            if (total == 0) {
                write_runlog(ERROR, "abnormal cpu info.\n");
                return -1;
            }

            return (int)((PERCENT * tmp) / total);
        }

        /* for smp, cpu0 is enough for iostat */
        if (cpu_nr > 1 && !strncmp(line, "cpu0", 4)) {
            if (GetCpuInfo(stat, cpu, line) != CM_SUCCESS) {
                (void)fclose(cpufp);
                return -1;
            }
            (void)fclose(cpufp);

            return 0;
        }
    }
    write_runlog(ERROR, "get cpu info fail.\n");
    (void)fclose(cpufp);

    return 0;
}

void ReadDiskstatsStatus(const char* device, IoStat* stat)
{
    FILE* iofp;
    char line[MAX_PATH_LEN] = {0};
    char dev_name[MAX_DEVICE_DIR] = {0};
    int i;
    uint64 rd_ios, rd_merges_or_rd_sec, rd_ticks_or_wr_sec;
    uint64 wr_ios, wr_merges, rd_sec_or_wr_ios, wr_sec;
    uint32 major, minor;
    uint32 rq_ticks, ios_pgr, wr_ticks;

    if ((iofp = fopen(FILE_DISKSTAT, "re")) == NULL) {
        write_runlog(ERROR, "failed to open file %s", FILE_DISKSTAT);
        return;
    }

    while (fgets(line, MAX_PATH_LEN, iofp) != NULL) {
        i = sscanf_s(line,
            "%u %u %s %lu %lu %lu %lu %lu %lu %lu %u %u %lu %u",
            &major,
            &minor,
            dev_name,
            MAX_DEVICE_DIR - 1,
            &rd_ios,
            &rd_merges_or_rd_sec,
            &rd_sec_or_wr_ios,
            &rd_ticks_or_wr_sec,
            &wr_ios,
            &wr_merges,
            &wr_sec,
            &wr_ticks,
            &ios_pgr,
            &stat->tot_ticks,
            &rq_ticks);
        check_sscanf_s_result(i, 14);
        securec_check_intval(i, (void)i);

        if (i == 14) {
            if (strcmp(dev_name, device) != 0) {
                continue;
            } else {
                break;
            }
        }
    }

    if (*dev_name == '\0') {
        write_runlog(LOG, "cannot get the information of the file %s.\n", FILE_DISKSTAT);
    }

    (void)fclose(iofp);
}

uint64 GetAverageValue(uint64 value1, uint64 value2, uint64 itv, uint32 unit)
{
    if (itv == 0) {
        return 0;
    }
    if ((value2 < value1) && (value1 <= 0xffffffff)) {
        /* Counter's type was unsigned long and has overflown */
        return (((value2 - value1) & 0xffffffff)) * unit / itv;
    } else {
        return ((value2 - value1) * unit / itv);
    }
}

static uint64 ReadDiskIOStat(const char* device, int cpu_nr, IoStat* oldIoStatus, bool needWriteLog)
{
    long ticks;
    IoStat ioStatus = {0};

    if ((ticks = sysconf(_SC_CLK_TCK)) == -1) {
        write_runlog(ERROR, "get ticks fail.\n");
        return 0;
    }

    uint32 hz = (unsigned int)ticks;

    (void)ReadCpuStatus(cpu_nr, &ioStatus, false);
    if (ioStatus.uptime == 0) {
        write_runlog(LOG, "get cpu time iz 0.\n");
        return 0;
    }
    uint64 totalTime = ioStatus.uptime - oldIoStatus->uptime;
    uint64 idleTime = ioStatus.idle - oldIoStatus->idle;
    if (oldIoStatus->uptime == 0 || totalTime == 0) {
        write_runlog(DEBUG1, "uptime is %lu, old_uptime is %lu,\n", ioStatus.uptime, oldIoStatus->uptime);
        oldIoStatus->uptime = ioStatus.uptime;
        oldIoStatus->idle = ioStatus.idle;
        ReadDiskstatsStatus(device, oldIoStatus);
        return 0;
    }

    /* get block io info for the specified device */
    ReadDiskstatsStatus(device, &ioStatus);

    /* get iostat */
    /* tot_ticks unit: ms, itv/HZ unit:s, util is percentage, unit: %. */
    const uint32 percent = 100;
    uint64 ioUtil = GetAverageValue(oldIoStatus->tot_ticks, ioStatus.tot_ticks, totalTime, hz) / 10;
    uint64 cpuUtil = percent * (totalTime - idleTime) / totalTime;

    oldIoStatus->tot_ticks = ioStatus.tot_ticks;
    oldIoStatus->uptime = ioStatus.uptime;
    oldIoStatus->idle = ioStatus.idle;

    if (ioUtil > PERCENT) {
        ioUtil = PERCENT;
    }

    if (!needWriteLog) {
        write_runlog(DEBUG1, "device %s, [Io util: %lu%%]\n", device, ioUtil);
        return ioUtil;
    }

    if (ioUtil > 60) {
        write_runlog(LOG, "device %s, [Cpu util: %lu%%], [Io util: %lu%%]\n", device, cpuUtil, ioUtil);
    } else {
        write_runlog(DEBUG1, "device %s, [Cpu util: %lu%%], [Io util: %lu%%]\n", device, cpuUtil, ioUtil);
    }
    return ioUtil;
}

static void CmGetDisk(const char* datadir, char* devicename, uint32 nameLen)
{
    char dfcommand[MAX_PATH_LEN] = {0};
    char devicePath[MAX_PATH_LEN] = {0};
    errno_t rc = snprintf_s(dfcommand, MAX_PATH_LEN, MAX_PATH_LEN - 1, "df -h %s", datadir);
    securec_check_intval(rc, (void)rc);
    const char* mode = "r";
    FILE* fp = popen(dfcommand, mode);
    if (fp == NULL) {
        write_runlog(ERROR, "execute %s fail\n", dfcommand);
        return;
    }
    char buf[CM_MAX_COMMAND_LONG_LEN] = {0};
    if (fgets(buf, sizeof(buf), fp) == NULL) {
        (void)pclose(fp);
        write_runlog(ERROR, "get first line fail.\n");
        return;
    } else {
        write_runlog(LOG, "first line is %s.\n", buf);
    }
    if (fgets(buf, sizeof(buf), fp) != NULL) {
        write_runlog(LOG, "second line is %s.\n", buf);
        uint32 length = (uint32)strlen(buf);
        if (length == 0) {
            (void)pclose(fp);
            write_runlog(LOG, "execute %s, result is empty.\n", dfcommand);
            return;
        }
        uint32 lengthDevice = 0;
        for (uint32 i = 0; i < length; i++) {
            if (lengthDevice >= MAX_PATH_LEN - 1) {
                (void)pclose(fp);
                write_runlog(LOG, "length is not enough for etcd data path device.\n");
                return;
            }
            /* read end */
            if ((buf[i] == ' ' || buf[i] == 10)) {
                break;
            }
            devicePath[lengthDevice] = buf[i];
            lengthDevice++;
        }
    } else {
        (void)pclose(fp);
        write_runlog(ERROR, "get second line fail.\n");
        return;
    }

    (void)pclose(fp);

    rc = snprintf_s(dfcommand, MAX_PATH_LEN, MAX_PATH_LEN - 1, "ls -l %s", devicePath);
    securec_check_intval(rc, (void)rc);

    fp = popen(dfcommand, mode);
    if (fp == NULL) {
        write_runlog(ERROR, "execute %s fail\n", dfcommand);
        return;
    } else {
        write_runlog(LOG, "execute %s success.\n", dfcommand);
    }
    if (fgets(buf, sizeof(buf), fp) != NULL) {
        uint32 length = (uint32)strlen(buf);
        bool findDevice = false;
        if (length == 0) {
            (void)pclose(fp);
            write_runlog(LOG, "execute %s, result is empty.\n", dfcommand);
            return;
        }
        uint lengthDevice = 0;
        for (uint32 i = 0; i < length; i++) {
            if (lengthDevice >= MAX_DEVICE_DIR - 1) {
                (void)pclose(fp);
                write_runlog(LOG, "length is not enough for etcd data path device.\n");
                return;
            }
            if (buf[i] != '>' && lengthDevice == 0 && !findDevice) {
                continue;
            }
            if (buf[i] == '>') {
                findDevice = true;
                continue;
            }
            if (findDevice && (lengthDevice != 0 || buf[i] == '/')) {
                if (buf[i] == '/') {
                    i++;
                }
                if ((buf[i] == ' ' || buf[i] == 10)) {
                    break;
                }
                if (i < length && lengthDevice < nameLen) {
                    devicename[lengthDevice] = buf[i];
                    lengthDevice++;
                }
            }
        }
        if (findDevice) {
            write_runlog(LOG, "device name is %s.\n", devicename);
            (void)pclose(fp);
            return;
        }
    }
    (void)pclose(fp);

    size_t buf_len = 0;
    struct mntent* ent;
    struct mntent tempEnt = {};

    FILE *mtfp = fopen(FILE_MOUNTS, "re");
    if (mtfp == NULL) {
        write_runlog(LOG, "cannot open file %s.\n", FILE_MOUNTS);
        return;
    }

    /* The buffer is too big, so it can not be stored in the stack space. */
    char *mntentBuffer = (char *)malloc(4 * FILENAME_MAX);
    if (mntentBuffer == NULL) {
        write_runlog(ERROR,
            "Failed to allocate memory: Out of memory. RequestSize=%d.\n", 4 * FILENAME_MAX);
        (void)fclose(mtfp);
        return;
    }

    while ((ent = getmntent_r(mtfp, &tempEnt, mntentBuffer, 4 * FILENAME_MAX)) != NULL) {
        buf_len = strlen(ent->mnt_fsname);
        /*
         * get the file system with type of ext* or xfs.
         * find the best fit for the data directory
         */
        size_t offset = strlen("/dev/");
        if (buf_len >= offset && strncmp(devicePath, "/dev/", offset) != 0) {
            offset = 0;
        }
        if (strncmp(ent->mnt_fsname, devicePath, buf_len) == 0 && strlen(datadir) >= buf_len &&
            buf_len == strlen(devicePath)) {
            rc = strncpy_s(devicename, MAX_DEVICE_DIR, ent->mnt_fsname + offset, strlen(ent->mnt_fsname + offset));
            if (rc != 0) {
                write_runlog(ERROR, "memcpy device name fail.\n");
                (void)fclose(mtfp);
                FREE_AND_RESET(mntentBuffer);
                return;
            } else {
                break;
            }
        }
    }

    write_runlog(LOG, "devicename is %s.\n", devicename);

    (void)fclose(mtfp);
    FREE_AND_RESET(mntentBuffer);
}

void GetDiskNameByDataPath(const char* datadir, char* devicename, uint32 nameLen)
{
    return CmGetDisk(datadir, devicename, nameLen);
}

static int GetCpuCount(void)
{
    char pathbuf[4096] = {0};
    int ret = 0;
    int cpucnt = 0;
    errno_t rc;

    if (access("/sys/devices/system", F_OK) == 0) {
        do {
            rc = snprintf_s(pathbuf, sizeof(pathbuf), sizeof(pathbuf) - 1, "/sys/devices/system/cpu/cpu%d", cpucnt);
            securec_check_intval(rc, (void)rc);

            ret = access(pathbuf, F_OK);
            if (ret == 0) {
                cpucnt++;
            }
        } while (ret == 0);
    } else if (access("/proc/cpuinfo", F_OK) == 0) {
        FILE* fd;

        if ((fd = fopen("/proc/cpuinfo", "re")) == NULL) {
            return -1;
        }

        while (fgets(pathbuf, sizeof(pathbuf), fd) != NULL) {
            if (strncmp("processor", pathbuf, strlen("processor")) == 0) {
                cpucnt++;
            }
        }
        (void)fclose(fd);
    }

    return cpucnt ? cpucnt : -1;
}
void etcd_disk_quota_check(const char *instanceName, const char *etcdData)
{
    char check_cmd[CM_MAX_COMMAND_LONG_LEN] = {0};
    const uint64 warningDiskQuota = 8160437862;    /* 8G * 95%. */
    int needWarning = 0;
    int rcs;

    rcs = sprintf_s(check_cmd, sizeof(check_cmd),
        "if [ `ls -l \"%s/member/snap/db\" | awk '{print $5}'` -ge %lu ]; then echo '1'; else echo '0'; fi;",
        etcdData, warningDiskQuota);
    securec_check_intval(rcs, (void)rcs);

    FILE *fp = popen(check_cmd, "r");
    if (fp == NULL) {
        write_runlog(ERROR, "etcd_disk_quota_check fail: %s\n", check_cmd);
        return;
    }

    rcs = fscanf_s(fp, "%d", &needWarning);
    if (rcs > 0) {
        /* used to control whether or not print local log */
        static int appear_cnt = 0;

        if (needWarning == 1) {
            if (appear_cnt++ % 5 == 0) {
                appear_cnt = 1;
                write_runlog(LOG, "etcd db files takes too much disk space.\n");
            }
            report_ddb_fail_alarm(ALM_AT_Fault, instanceName, 2, DB_ETCD);
        } else {
            if (appear_cnt != 0) {
                write_runlog(LOG, "etcd db files takes normal disk space.\n");
                appear_cnt = 0;
            }
            report_ddb_fail_alarm(ALM_AT_Resume, instanceName, 2, DB_ETCD);
        }
    } else {
        write_runlog(LOG, "Failed to get etcd db files's disk space.\n");
    }
    (void)pclose(fp);
}

static void GetDdbCfgApi(DrvApiInfo *drvApiInfo, ServerSocket *server, uint32 serverLen)
{
    drvApiInfo->nodeNum = serverLen - 1;
    drvApiInfo->serverList = server;
    drvApiInfo->serverLen = serverLen;
    drvApiInfo->modId = MOD_CMA;
    drvApiInfo->nodeId = g_currentNode->node;

    drvApiInfo->client_t.tlsPath = &g_tlsPath;
    drvApiInfo->timeOut = DDB_DEFAULT_TIMEOUT;
}

static void SetServerSocketWithEtcdInfo(ServerSocket *server, staticNodeConfig *node)
{
    server->nodeIdInfo.azName = node->azName;
    server->nodeIdInfo.nodeId = node->node;
    server->nodeIdInfo.instd = node->etcdId;
    server->nodeInfo.nodeName = node->etcdName;
    server->nodeInfo.len = CM_NODE_NAME;
    server->host = node->etcdClientListenIPs[0];
    server->port = node->etcdClientListenPort;
}

int CheckCertFilePermission(const char *certFile)
{
    struct stat buf;
    if (stat(certFile, &buf) != 0) {
        write_runlog(WARNING, "Try to stat cert key file \"%s\" failed!\n", certFile);
        return -1;
    }
    if (!S_ISREG(buf.st_mode) || (buf.st_mode & (S_IRWXG | S_IRWXO)) || ((buf.st_mode & S_IRWXU) == S_IRWXU)) {
        write_runlog(WARNING, "The file \"%s\" permission should be u=rw(600) or less.\n", certFile);
        return -1;
    }
    return 0;
}
bool EtcdCertFileExpire(const char *certFile)
{
    int rcs;
    const int expireDays = 90;
    int expireSeconds = expireDays * 24 * 60 * 60;
    char command[CM_PATH_LENGTH] = {0};
    char result[CM_PATH_LENGTH] = {0};
    const char *certExpire = "Certificate will expire";
    rcs = snprintf_s(command, CM_PATH_LENGTH, CM_PATH_LENGTH - 1, "openssl x509 -in %s -checkend %d 2>&1 &", certFile,
        expireSeconds);
    securec_check_intval(rcs, (void)rcs);
    if (!ExecuteCmdWithResult(command, result, CM_PATH_LENGTH)) {
        write_runlog(WARNING, "Execute check etcd cert file expire cmd %s failed, result=%s\n", command, result);
        return false;
    }
    if (strstr(result, certExpire) != NULL) {
        write_runlog(WARNING,
            "etcd cert file %s may has been expired or will be expired in less %d days, please check!\n", certFile,
            expireDays);
        return true;
    }
    return false;
}
void CheckEtcdClientCertFile()
{
    (void)CheckCertFilePermission(g_tlsPath.caFile);
    (void)CheckCertFilePermission(g_tlsPath.crtFile);
    (void)CheckCertFilePermission(g_tlsPath.keyFile);
    (void)EtcdCertFileExpire(g_tlsPath.caFile);
}
void CheckEtcdServerCertFile()
{
    int rcs;
    char caFile[MAX_PATH_LEN] = {0};
    char keyFile[MAX_PATH_LEN] = {0};
    rcs = snprintf_s(caFile, MAX_PATH_LEN, MAX_PATH_LEN - 1, "%s/etcd.crt", g_currentNode->etcdDataPath);
    securec_check_intval(rcs, (void)rcs);
    rcs = snprintf_s(keyFile, MAX_PATH_LEN, MAX_PATH_LEN - 1, "%s/etcd.key", g_currentNode->etcdDataPath);
    securec_check_intval(rcs, (void)rcs);
    (void)CheckCertFilePermission(caFile);
    (void)CheckCertFilePermission(keyFile);
    (void)EtcdCertFileExpire(caFile);
}
void CheckEtcdCertFile()
{
    static int checkTimes = 0;
    const int everyCheckTime = 60;
    // every 10 or 5 minutes, check etcd cert file permission or expire time in ETCDStatusCheckMain thread
    // just to avoid warning log print too many
    if (checkTimes >= everyCheckTime) {
        CheckEtcdClientCertFile();
        CheckEtcdServerCertFile();
        checkTimes = 0;
    } else {
        checkTimes++;
    }
}
void* ETCDStatusCheckMain(void* arg)
{
    thread_name = "ETCD_CHECK";
    pthread_t threadId = pthread_self();
    write_runlog(LOG, "etcd status check thread start, threadid %lu.\n", threadId);
    int etcdReportFre = ETCD_NODE_UNHEALTH_FRE;
    char devicename[MAX_DEVICE_DIR] = {0};
    CmGetDisk(g_currentNode->etcdDataPath, devicename, MAX_DEVICE_DIR);
    uint64 devicenamelength = strlen(devicename);
    IoStat ioStatus = {0};
    int cpu_nr = GetCpuCount();
    errno_t rc;
    cm_query_instance_status cm_query_instance_status_content = {0};
    cm_query_instance_status_content.nodeId = g_currentNode->node;
    cm_query_instance_status_content.msg_type = MSG_CM_QUERY_INSTANCE_STATUS;
    cm_query_instance_status_content.msg_step = QUERY_STATUS_CMAGENT_STEP;
    cm_query_instance_status_content.instanceType = PROCESS_ETCD;
    cm_query_instance_status_content.pending = false;

    char instanceName[CM_NODE_NAME] = {0};
    rc = snprintf_s(
        instanceName, sizeof(instanceName), sizeof(instanceName) - 1, "%s_%u", "etcd", g_currentNode->etcdId);
    securec_check_intval(rc, (void)rc);
    char command[MAXPGPATH * 2] = {0};
    int checkInvalidEtcdTimes = 0;
    const uint32 serverLen = 2;
    ServerSocket server[serverLen] = {{0}};
    SetServerSocketWithEtcdInfo(&server[0], g_currentNode);
    server[1].host = NULL;
    status_t st = CM_SUCCESS;
    DdbInitConfig config = {DB_ETCD};
    GetDdbCfgApi(&config.drvApiInfo, server, serverLen);
    DdbNodeState nodeState;
    for (;;) {
        if (devicenamelength > 0) {
            (void)ReadDiskIOStat(devicename, cpu_nr, &ioStatus, true);
        }
        CheckEtcdCertFile();

        if (g_shutdownRequest || (agent_cm_server_connect == NULL) || g_exitFlag) {
            write_runlog(LOG, "receive exit request in cma ETCDStatusCheckMain.\n");
            cm_sleep(5);
            continue;
        }
        if (cpu_nr <= 0) {
            cpu_nr = GetCpuCount();
        }
        int tryTime = 0;
        int tryTime1 = 0;

        rc = memset_s(&nodeState, sizeof(DdbNodeState), 0, sizeof(DdbNodeState));
        securec_check_errno(rc, (void)rc);

        DdbConn dbCon = {0};
        st = InitDdbConn(&dbCon, &config);
        if (st != CM_SUCCESS) {
            (void)pthread_rwlock_wrlock(&(g_etcdReportMsg.lk_lock));
            cm_query_instance_status_content.status = CM_ETCD_DOWN;
            rc = memcpy_s((void*)&(g_etcdReportMsg.report_msg),
                sizeof(cm_query_instance_status_content),
                (void*)&cm_query_instance_status_content,
                sizeof(cm_query_instance_status_content));
            securec_check_errno(rc, (void)rc);
            (void)pthread_rwlock_unlock(&(g_etcdReportMsg.lk_lock));
            write_runlog(ERROR, "etcd open failed when query etcd status. %s\n", DdbGetLastError(&dbCon));
            cm_sleep(agent_check_interval);
            continue;
        }
        do {
            st = DdbInstanceState(&dbCon, g_currentNode->etcdName, &nodeState);
            if (st != CM_SUCCESS) {
                write_runlog(FATAL, "get ddb instance state failed, error is %s\n", DdbGetLastError(&dbCon));
            }

            tryTime1++;
            if (st != CM_SUCCESS) {
                if (nodeState.health == DDB_STATE_HEALTH) {
                    do {
                        st = DdbInstanceState(&dbCon, g_currentNode->etcdName, &nodeState);
                        tryTime++;
                        if (st != CM_SUCCESS) {
                            write_runlog(FATAL, "ddb instance is health, get state failed, error is %s\n",
                                DdbGetLastError(&dbCon));
                        }
                    } while ((st != CM_SUCCESS) && tryTime <= ETCD_CHECK_TIMES);
                }
            }
        } while ((st != CM_SUCCESS) && tryTime1 <= ETCD_CHECK_TIMES);

        if ((st != CM_SUCCESS) || (nodeState.health != DDB_STATE_HEALTH && nodeState.role == DDB_ROLE_LEADER)) {
            checkInvalidEtcdTimes++;
        } else {
            checkInvalidEtcdTimes = 0;
        }
        if (checkInvalidEtcdTimes >= CHECK_INVALID_ETCD_TIMES) {
            write_runlog(
                ERROR, "can't get majority etcd state, but local is unhealthy and leader, will kill local etcd now.\n");
            checkInvalidEtcdTimes = 0;
            rc = strcpy_s(command, 2 * MAXPGPATH, SYSTEMQUOTE "killall etcd > /dev/null  2>&1 &" SYSTEMQUOTE);
            securec_check_errno(rc, (void)rc);
            int rct = system(command);
            if (rct != -1) {
                write_runlog(LOG, "killall etcd result is %d, shell result is %d.\n", rc, WEXITSTATUS(rc));
            } else {
                char error_buffer[ERROR_LIMIT_LEN] = {0};
                (void)strerror_r(errno, error_buffer, ERROR_LIMIT_LEN);

                write_runlog(ERROR, "Failed to call the system function: error=\"[%d] %s\","
                    " function=\"%s\", command=\"%s\".\n", errno, error_buffer, "system", command);
            }
        }

        if (DdbFreeConn(&dbCon) != CM_SUCCESS) {
            write_runlog(WARNING, "etcd_close failed,%s\n", DdbGetLastError(&dbCon));
        }
        if (st != CM_SUCCESS) {
            cm_query_instance_status_content.status = CM_ETCD_DOWN;
        } else {
            if (nodeState.role == DDB_ROLE_LEADER) {
                write_runlog(DEBUG1, "etcd state is StateLeader.\n");
                cm_query_instance_status_content.status = CM_ETCD_LEADER;
            } else if (nodeState.role == DDB_ROLE_FOLLOWER) {
                write_runlog(DEBUG1, "etcd state is StateFollower.\n");
                cm_query_instance_status_content.status = CM_ETCD_FOLLOWER;
            }
        }
        if (cm_query_instance_status_content.status == CM_ETCD_DOWN) {
            if (etcdReportFre > ETCD_NODE_UNHEALTH_FRE) {
                etcdReportFre = ETCD_NODE_UNHEALTH_FRE;
            }
            if (etcdReportFre > 0) {
                etcdReportFre--;
            }
            if (etcdReportFre <= 0) {
                /* report the alarm. */
                report_ddb_fail_alarm(ALM_AT_Fault, instanceName, 1, DB_ETCD);
            }
        } else {
            etcdReportFre = ETCD_NODE_UNHEALTH_FRE;
            report_ddb_fail_alarm(ALM_AT_Resume, instanceName, 1, DB_ETCD);
        }
        (void)pthread_rwlock_wrlock(&(g_etcdReportMsg.lk_lock));
        rc = memcpy_s((void*)&(g_etcdReportMsg.report_msg), sizeof(cm_query_instance_status_content),
            (void*)&cm_query_instance_status_content,
            sizeof(cm_query_instance_status_content));
        securec_check_errno(rc, (void)rc);
        (void)pthread_rwlock_unlock(&(g_etcdReportMsg.lk_lock));

        /* check and warn etcd db file's disk space */
        etcd_disk_quota_check(instanceName, g_currentNode->etcdDataPath);

        cm_sleep(10);
        continue;
    }
}

/* agent send report_msg to cm_server */
void kerberos_status_check_and_report()
{
    if (agent_cm_server_connect == NULL) {
        return;
    }
    char kerberosConfigPath[MAX_PATH_LEN] = {0};
    if (cmagent_getenv("MPPDB_KRB5_FILE_PATH", kerberosConfigPath, sizeof(kerberosConfigPath)) != EOK) {
        write_runlog(DEBUG1, "kerberos_status_check_and_report: MPPDB_KRB5_FILE_PATH get fail.\n");
        return;
    }

    struct stat stat_buf = {0};
    if (stat(kerberosConfigPath, &stat_buf) != 0) {
        write_runlog(DEBUG1, "kerberos_status_check_and_report: kerberos config file not exist.\n");
        return;
    }
    agent_to_cm_kerberos_status_report reportMsg = {0};
    (void)pthread_rwlock_wrlock(&(g_kerberosReportMsg.lk_lock));
    errno_t rc = memcpy_s(&reportMsg, sizeof(agent_to_cm_kerberos_status_report),
        &(g_kerberosReportMsg.report_msg), sizeof(agent_to_cm_kerberos_status_report));
    securec_check_errno(rc, (void)rc);
    (void)pthread_rwlock_unlock(&(g_kerberosReportMsg.lk_lock));

    PushMsgToCmsSendQue((char *)&reportMsg, (uint32)sizeof(agent_to_cm_kerberos_status_report), "kerberos status");
}

static void SendDnReportMsg(const DnStatus *pkgDnStatus, uint32 datanodeId)
{
    if (undocumentedVersion != 0 && undocumentedVersion < SUPPORT_IPV6_VERSION) {
        agent_to_cm_datanode_status_report_ipv4 reportMsg = {0};
        AgentToCmDatanodeStatusReportV2ToV1(&pkgDnStatus->reportMsg, &reportMsg);
        write_runlog(DEBUG5, "dn(%u) reportMsg will send to cms.\n", datanodeId);
        PushMsgToCmsSendQue((char *)&reportMsg, (uint32)sizeof(agent_to_cm_datanode_status_report_ipv4), "dn report");
    } else {
        agent_to_cm_datanode_status_report reportMsg = {0};
        errno_t rc = memcpy_s(&reportMsg, sizeof(agent_to_cm_datanode_status_report),
            &pkgDnStatus->reportMsg, sizeof(agent_to_cm_datanode_status_report));
        securec_check_errno(rc, (void)rc);
        write_runlog(DEBUG5, "dn(%u) reportMsg will send to cms.\n", datanodeId);
        PushMsgToCmsSendQue((char *)&reportMsg, (uint32)sizeof(agent_to_cm_datanode_status_report), "dn report");
    }
}

static void SendBarrierMsg(const DnStatus *pkgDnStatus, uint32 datanodeId)
{
    AgentToCmBarrierStatusReport barrierMsg = {0};
    errno_t rc = memcpy_s(&barrierMsg, sizeof(AgentToCmBarrierStatusReport),
        &pkgDnStatus->barrierMsg, sizeof(AgentToCmBarrierStatusReport));
    securec_check_errno(rc, (void)rc);

    write_runlog(DEBUG5, "dn(%u) barrier(%d) will send to cms.\n", datanodeId, (int)pkgDnStatus->barrierMsgType);
    PushMsgToCmsSendQue((char *)&barrierMsg, (uint32)sizeof(AgentToCmBarrierStatusReport), "dn barrier");
}

static void SendLpInfoMsg(const DnStatus *pkgDnStatus, uint32 datanodeId)
{
    AgentCmDnLocalPeer lpInfo = {0};
    errno_t rc = memcpy_s(&lpInfo, sizeof(AgentCmDnLocalPeer), &pkgDnStatus->lpInfo, sizeof(AgentCmDnLocalPeer));
    securec_check_errno(rc, (void)rc);

    write_runlog(DEBUG5, "dn(%u) dnLocalPeer will send to cms.\n", datanodeId);
    PushMsgToCmsSendQue((char *)&lpInfo, (uint32)sizeof(AgentCmDnLocalPeer), "dnLocalPeer");
}

static void SendDiskUsageMsg(const DnStatus *pkgDnStatus, uint32 datanodeId)
{
    AgentToCmDiskUsageStatusReport diskUsageMsg = {0};
    errno_t rc = memcpy_s(&diskUsageMsg, sizeof(AgentToCmDiskUsageStatusReport),
        &pkgDnStatus->diskUsageMsg, sizeof(AgentToCmDiskUsageStatusReport));
    securec_check_errno(rc, (void)rc);

    write_runlog(DEBUG5, "dn(%u) dnDiskUsage will send to cms.\n", datanodeId);
    PushMsgToCmsSendQue((char *)&diskUsageMsg, (uint32)sizeof(AgentToCmDiskUsageStatusReport), "dnDiskUsage");
}

static void SendFloatIpMsg(const CmaDnFloatIpInfo *floatIpInfo, uint32 dnId)
{
    if (!IsNeedCheckFloatIp() || (agent_backup_open != CLUSTER_PRIMARY)) {
        return;
    }
    if (floatIpInfo->info.count == 0) {
        return;
    }
    write_runlog(DEBUG5, "dn(%u) floatIpMsg will send to cms.\n", dnId);
    PushMsgToCmsSendQue((const char *)floatIpInfo, (uint32)sizeof(CmaDnFloatIpInfo), "dn floatIpMsg");
}

static void SendDnReportMsgCore(const DnStatus *pkgDnStatus, uint32 datanodeId, AgentToCmserverDnSyncList *syncListMsg)
{
    SendDnReportMsg(pkgDnStatus, datanodeId);
    SendFloatIpMsg(&(pkgDnStatus->floatIpInfo), datanodeId);
    if (g_clusterType == V3SingleInstCluster) {
        return;
    }
#if ((defined(ENABLE_MULTIPLE_NODES)) || (defined(ENABLE_PRIVATEGAUSS)))
    write_runlog(DEBUG5, "dn(%u) syncListMsg will send to cms.\n", datanodeId);
    PushMsgToCmsSendQue((char *)syncListMsg, (uint32)sizeof(AgentToCmserverDnSyncList), "dn syncListMsg");
#endif
    if (pkgDnStatus->barrierMsgType == MSG_AGENT_CM_DATANODE_INSTANCE_BARRIER) {
        SendBarrierMsg(pkgDnStatus, datanodeId);
        return;
    }
    SendDiskUsageMsg(pkgDnStatus, datanodeId);
    // only cascade standby cannot report lpInfo
    if (pkgDnStatus->reportMsg.receive_status.local_role != INSTANCE_ROLE_CASCADE_STANDBY) {
        return;
    }

    SendLpInfoMsg(pkgDnStatus, datanodeId);
}

static void DnStatusFinalProcessing(DnStatus* pkgDnStatus, uint32 dnId)
{
    if (pkgDnStatus->reportMsg.local_status.db_state == INSTANCE_HA_STATE_NORMAL ||
        pkgDnStatus->reportMsg.local_status.db_state == INSTANCE_HA_STATE_NEED_REPAIR ||
        pkgDnStatus->reportMsg.local_status.db_state == INSTANCE_HA_STATE_UNKONWN ||
        (pkgDnStatus->reportMsg.local_status.db_state == INSTANCE_HA_STATE_DEMOTING &&
         !g_isStorageWithDMSorDSS)) {
        g_dnRoleForPhonyDead[dnId] = pkgDnStatus->reportMsg.local_status.local_role;
    } else {
        g_dnRoleForPhonyDead[dnId] = INSTANCE_ROLE_INIT;
    }
    if (g_dnPhonyDeadD[dnId] || g_dnCore[dnId]) {
        pkgDnStatus->reportMsg.local_status.local_role = INSTANCE_ROLE_UNKNOWN;
        write_runlog(WARNING, "datenode phony dead D or Core, set local_role Unknown\n");
    }
    if (g_dnPingFault[dnId]) {
        pkgDnStatus->reportMsg.local_status.local_role = INSTANCE_ROLE_UNKNOWN;
        pkgDnStatus->reportMsg.local_status.db_state = INSTANCE_HA_STATE_UNKONWN;
        write_runlog(WARNING, "datenode ping fault, set local_role Unknown\n");
    }
}

static void CopyDnReportMsg(AgentToCmserverDnSyncList *syncList, uint32 ii)
{
    (void)pthread_rwlock_wrlock(&(g_dnSyncListInfo[ii].lk_lock));
    errno_t rcs = memcpy_s(syncList, sizeof(AgentToCmserverDnSyncList), &(g_dnSyncListInfo[ii].dnSyncListMsg),
        sizeof(AgentToCmserverDnSyncList));
    securec_check_errno(rcs, (void)rcs);
    g_dnSyncListInfo[ii].dnSyncListMsg.syncDone = FAILED_SYNC_DATA;
    (void)pthread_rwlock_unlock(&(g_dnSyncListInfo[ii].lk_lock));
}

void DatanodeStatusReport(void)
{
    errno_t rc;
    for (uint32 ii = 0; ii < g_currentNode->datanodeCount; ii++) {
        if (agent_cm_server_connect == NULL) {
            continue;
        }

        (void)pthread_rwlock_wrlock(&(g_dnReportMsg[ii].lk_lock));
        if (g_dnReportMsg[ii].dnStatus.reportMsg.local_status.local_role == INSTANCE_ROLE_PENDING ||
            (g_dnReportMsg[ii].dnStatus.reportMsg.processStatus != INSTANCE_PROCESS_RUNNING &&
                g_dnReportMsg[ii].dnStatus.reportMsg.connectStatus != AGENT_TO_INSTANCE_CONNECTION_OK)) {
            if (g_dnPhonyDeadTimes[ii] != 0) {
                g_dnPhonyDeadTimes[ii] = 0;
                write_runlog(
                    LOG, "reset dn(%u) phony dead time to zero.\n", g_dnReportMsg[ii].dnStatus.reportMsg.instanceId);
            }
        }
        if (g_dnReportMsg[ii].dnStatus.reportMsg.connectStatus == AGENT_TO_INSTANCE_CONNECTION_OK) {
            DnStatus dnStatus;
            rc = memcpy_s(&(dnStatus), sizeof(DnStatus), &(g_dnReportMsg[ii].dnStatus), sizeof(DnStatus));
            securec_check_errno(rc, (void)rc);
            (void)pthread_rwlock_unlock(&(g_dnReportMsg[ii].lk_lock));

            AgentToCmserverDnSyncList syncList = {0};
            CopyDnReportMsg(&syncList, ii);
            DnStatusFinalProcessing(&dnStatus, ii);
            dnStatus.reportMsg.phony_dead_times = g_dnPhonyDeadTimes[ii];

            SendDnReportMsgCore(&dnStatus, g_currentNode->datanode[ii].datanodeId, &syncList);
        } else if (g_dnReportMsg[ii].dnStatus.reportMsg.processStatus == INSTANCE_PROCESS_RUNNING) {
            (void)pthread_rwlock_unlock(&(g_dnReportMsg[ii].lk_lock));
            g_dnRoleForPhonyDead[ii] = INSTANCE_ROLE_INIT;
            agent_to_cm_heartbeat hbMsg = {0};
            hbMsg.msg_type = (int)MSG_AGENT_CM_HEARTBEAT;
            hbMsg.node = g_currentNode->node;
            hbMsg.instanceId = g_currentNode->datanode[ii].datanodeId;
            hbMsg.instanceType = INSTANCE_TYPE_DATANODE;

            PushMsgToCmsSendQue((char *)&hbMsg, (uint32)sizeof(agent_to_cm_heartbeat), "dn heartbeat");
        } else {
            DnStatus pkgDnStatus;
            rc = memcpy_s(&(pkgDnStatus), sizeof(DnStatus), &(g_dnReportMsg[ii].dnStatus), sizeof(DnStatus));
            securec_check_errno(rc, (void)rc);
            (void)pthread_rwlock_unlock(&(g_dnReportMsg[ii].lk_lock));

            AgentToCmserverDnSyncList syncList = {0};
            CopyDnReportMsg(&syncList, ii);
            g_dnRoleForPhonyDead[ii] = pkgDnStatus.reportMsg.local_status.local_role;

            SendDnReportMsgCore(&pkgDnStatus, g_currentNode->datanode[ii].datanodeId, &syncList);
        }
    }
}

void fenced_UDF_status_check_and_report(void)
{
    if (agent_cm_server_connect == NULL) {
        return;
    }

    agent_to_cm_fenced_UDF_status_report reportMsg = {0};
    reportMsg.msg_type = (int)MSG_AGENT_CM_FENCED_UDF_INSTANCE_STATUS;
    reportMsg.nodeid = g_nodeId;

    if (!g_fencedUdfStopped) {
        reportMsg.status = INSTANCE_ROLE_NORMAL;
    } else {
        reportMsg.status = INSTANCE_ROLE_UNKNOWN;
    }
    write_runlog(DEBUG5, "node(%u) fenced UDF status will send to cms.\n", reportMsg.nodeid);
    PushMsgToCmsSendQue((char *)&reportMsg, (uint32)sizeof(agent_to_cm_fenced_UDF_status_report), "fenced UDF status");
}

void InitReportMsg(agent_to_cm_datanode_status_report *reportMsg, int index)
{
    errno_t rc =
        memset_s(reportMsg, sizeof(agent_to_cm_datanode_status_report), 0, sizeof(agent_to_cm_datanode_status_report));
    securec_check_errno(rc, (void)rc);
    reportMsg->msg_type = MSG_AGENT_CM_DATA_INSTANCE_REPORT_STATUS;
    reportMsg->node = g_currentNode->node;
    reportMsg->instanceId = g_currentNode->datanode[index].datanodeId;
    reportMsg->instanceType = INSTANCE_TYPE_DATANODE;
    reportMsg->dn_restart_counts = g_dnReportMsg[index].dnStatus.reportMsg.dn_restart_counts;
}

void InitDnLocalPeerMsg(AgentCmDnLocalPeer *lpInfo, int32 index)
{
    errno_t rc = memset_s(lpInfo, sizeof(AgentCmDnLocalPeer), 0, sizeof(AgentCmDnLocalPeer));
    securec_check_errno(rc, (void)rc);
    lpInfo->msgType = (int32)MSG_AGENT_CM_DATANODE_LOCAL_PEER;
    lpInfo->instanceId = g_currentNode->datanode[index].datanodeId;
    lpInfo->node = g_currentNode->node;
    lpInfo->instanceType = INSTANCE_TYPE_DATANODE;
}

static void SetDnBaseMsg(BaseInstInfo *baseInfo, int32 index, int32 msgType)
{
    baseInfo->msgType = msgType;
    baseInfo->instId = g_currentNode->datanode[index].datanodeId;
    baseInfo->node = g_currentNode->node;
    baseInfo->instType = INSTANCE_TYPE_DATANODE;
}

static void InitDnFloatIpMsg(CmaDnFloatIpInfo *ipInfo, int32 index)
{
    errno_t rc = memset_s(ipInfo, sizeof(CmaDnFloatIpInfo), 0, sizeof(CmaDnFloatIpInfo));
    securec_check_errno(rc, (void)rc);
    SetDnBaseMsg(&(ipInfo->baseInfo), index, (int32)MSG_AGENT_CM_FLOAT_IP);
}

void InitDNStatus(DnStatus *dnStatus, int i)
{
    InitReportMsg(&dnStatus->reportMsg, i);
    InitDnLocalPeerMsg(&(dnStatus->lpInfo), i);
    InitDnFloatIpMsg(&(dnStatus->floatIpInfo), i);
}

void InitWrFloatIp(CmaWrFloatIp* wrFloatIp, int32 index)
{
    wrFloatIp->msgType = MSG_AGENT_CM_WR_FLOAT_IP;
    wrFloatIp->node = g_nodeId;
    wrFloatIp->instId = RES_INSTANCE_ID_MIN + g_currentNode ->node;
    wrFloatIp->count = 0;
    for (uint32 i = 0; i < MAX_FLOAT_IP_COUNT; i++) {
        wrFloatIp->netState[i] = NETWORK_STATE_UNKNOWN;
    }
}

void ReportCmsWrFloatIp(CmaWrFloatIp* wrFloatIp, int32 index)
{
    wrFloatIp->node = g_nodeId;
    PushMsgToCmsSendQue((char *)wrFloatIp, (uint32)sizeof(CmaWrFloatIp), "wr float ip");
}

static void ChangeLocalRoleInBackup(int dnIdx, int *localDnRole)
{
    if (*localDnRole == INSTANCE_ROLE_PRIMARY) {
        write_runlog(ERROR, "dn_%u is Primary in cluster standby.\n", g_currentNode->datanode[dnIdx].datanodeId);
        immediate_stop_one_instance(g_currentNode->datanode[dnIdx].datanodeLocalDataPath, INSTANCE_DN);
    }
    if (*localDnRole == INSTANCE_ROLE_MAIN_STANDBY) {
        *localDnRole = INSTANCE_ROLE_PRIMARY;
    } else if (*localDnRole == INSTANCE_ROLE_CASCADE_STANDBY) {
        *localDnRole = INSTANCE_ROLE_STANDBY;
    }
}

static void SendAlarmMsg(int alarmIndex, const char* logicClusterName, const char *instanceName, AlarmType type)
{
    AlarmAdditionalParam tempAdditionalParam;
    if ((g_abnormalAlarmList != NULL) && (!g_suppressAlarm)) {
        /* fill the alarm message */
        if (type == ALM_AT_Resume) {
            WriteAlarmAdditionalInfo(&tempAdditionalParam,
                instanceName, "", "", logicClusterName,
                &(g_abnormalAlarmList[alarmIndex]), ALM_AT_Resume);
            /* report the alarm */
            AlarmReporter(&(g_abnormalAlarmList[alarmIndex]), ALM_AT_Resume, &tempAdditionalParam);
        } else {
            WriteAlarmAdditionalInfo(&tempAdditionalParam,
                instanceName, "", "", logicClusterName,
                &(g_abnormalAlarmList[alarmIndex]), ALM_AT_Fault, instanceName);
            /* report the alarm */
            AlarmReporter(&(g_abnormalAlarmList[alarmIndex]), ALM_AT_Fault, &tempAdditionalParam);
        }
    }
    return;
}

static void CheckAlmLocalPrimary(
    DnStatus *dnStatus, int alarmIndex, const char *logicClusterName, const char *instanceName)
{
    bool peerRoleStandby = (dnStatus->reportMsg.sender_status[0].peer_role == INSTANCE_ROLE_STANDBY ||
        (agent_backup_open == CLUSTER_STREAMING_STANDBY &&
        dnStatus->reportMsg.sender_status[0].peer_role == INSTANCE_ROLE_CASCADE_STANDBY));

    if (peerRoleStandby) {
        /* the primary dn and standby dn instance are NORMAL */
        if ((dnStatus->reportMsg.local_status.db_state == INSTANCE_HA_STATE_NORMAL) &&
            ((dnStatus->reportMsg.sender_status[0].peer_state == INSTANCE_HA_STATE_NORMAL) ||
            (dnStatus->reportMsg.sender_status[0].peer_state == INSTANCE_HA_STATE_CATCH_UP))) {
            SendAlarmMsg(alarmIndex, logicClusterName, instanceName, ALM_AT_Resume);
        } else {
            SendAlarmMsg(alarmIndex, logicClusterName, instanceName, ALM_AT_Fault);
        }
    } else {
        if (dnStatus->reportMsg.sender_status[0].peer_role != INSTANCE_ROLE_PENDING) {
            SendAlarmMsg(alarmIndex, logicClusterName, instanceName, ALM_AT_Fault);
        }
    }
    return;
}

static void CheckAlmLocalStandby(
    DnStatus *dnStatus, int alarmIndex, const char *logicClusterName, const char *instanceName)
{
    bool peerRolePrimary = (dnStatus->reportMsg.receive_status.peer_role == INSTANCE_ROLE_PRIMARY ||
        (agent_backup_open == CLUSTER_STREAMING_STANDBY &&
        dnStatus->reportMsg.receive_status.peer_role == INSTANCE_ROLE_MAIN_STANDBY));

    if (peerRolePrimary) {
        /* the standby dn and primary dn instance are NORMAL */
        if ((dnStatus->reportMsg.receive_status.peer_state == INSTANCE_HA_STATE_NORMAL) &&
            ((dnStatus->reportMsg.local_status.db_state == INSTANCE_HA_STATE_NORMAL) ||
            (dnStatus->reportMsg.local_status.db_state == INSTANCE_HA_STATE_CATCH_UP))) {
            SendAlarmMsg(alarmIndex, logicClusterName, instanceName, ALM_AT_Resume);
        } else {
            SendAlarmMsg(alarmIndex, logicClusterName, instanceName, ALM_AT_Fault);
        }
    } else {
        if (dnStatus->reportMsg.receive_status.peer_role != INSTANCE_ROLE_PENDING) {
            SendAlarmMsg(alarmIndex, logicClusterName, instanceName, ALM_AT_Fault);
        }
    }
    return;
}

void* DNStatusCheckMain(void *arg)
{
    DnStatus dnStatus;
    int32 i = *(int32*)arg;
    errno_t rc;
    pthread_t threadId = pthread_self();
    uint32 dn_restart_count_check_time = 0;
    uint32 dn_restart_count_check_time_in_hour = 0;
    g_dnReportMsg[i].dnStatus.reportMsg.dn_restart_counts = 0;
    uint32 check_dn_sql5_timer = g_check_dn_sql5_interval;
    char* logicClusterName = NULL;
    char instanceName[CM_NODE_NAME] = {0};
    int alarmIndex = i;

    write_runlog(LOG, "dn(%d) status check thread start, threadid %lu.\n", i, threadId);

    int checkDummyTimes = CHECK_DUMMY_STATE_TIMES;

    int ret = snprintf_s(instanceName, sizeof(instanceName), sizeof(instanceName) - 1,
        "%s_%u", "dn", g_currentNode->datanode[i].datanodeId);
    securec_check_intval(ret, (void)ret);
    int32 running = PROCESS_UNKNOWN;

    int index = -1;
    AddThreadActivity(&index, threadId);

    for (;;) {
        set_thread_state(threadId);
        struct stat instance_stat_buf = {0};
        struct stat cluster_stat_buf = {0};

        if (g_shutdownRequest || g_exitFlag || g_enableWalRecord) {
            cm_sleep(5);
            continue;
        }

        InitDNStatus(&dnStatus, i);
        running = check_one_instance_status(GetDnProcessName(), g_currentNode->datanode[i].datanodeLocalDataPath, NULL);
        if (g_currentNode->datanode[i].datanodeRole != DUMMY_STANDBY_DN) {
            ret = DatanodeStatusCheck(&dnStatus, (uint32)i, running);
        }

        if (ret < 0 || g_currentNode->datanode[i].datanodeRole == DUMMY_STANDBY_DN) {
            if (g_currentNode->datanode[i].datanodeRole != DUMMY_STANDBY_DN) {
                write_runlog(ERROR, "DatanodeStatusCheck failed, ret=%d\n", ret);
            }

            if (g_currentNode->datanode[i].datanodeRole == DUMMY_STANDBY_DN &&
                dnStatus.reportMsg.processStatus != INSTANCE_PROCESS_RUNNING && running != PROCESS_RUNNING) {
                checkDummyTimes = CHECK_DUMMY_STATE_TIMES;
            }
            if (running == PROCESS_RUNNING) {
                if (g_currentNode->datanode[i].datanodeRole == DUMMY_STANDBY_DN && checkDummyTimes > 0) {
                    checkDummyTimes--;
                }
                if (checkDummyTimes <= 0 || g_currentNode->datanode[i].datanodeRole != DUMMY_STANDBY_DN) {
                    dnStatus.reportMsg.processStatus = INSTANCE_PROCESS_RUNNING;
                }
            } else {
                write_runlog(LOG, "set %u on offline.\n", dnStatus.reportMsg.instanceId);
                char instance_manual_start_path[MAX_PATH_LEN] = {0};

                dnStatus.reportMsg.processStatus = INSTANCE_PROCESS_DIED;
                dnStatus.reportMsg.local_status.local_role = INSTANCE_ROLE_UNKNOWN;
                rc = snprintf_s(instance_manual_start_path, MAX_PATH_LEN, MAX_PATH_LEN - 1,
                    "%s_%u",
                    g_cmInstanceManualStartPath,
                    g_currentNode->datanode[i].datanodeId);
                securec_check_intval(rc, (void)rc);
                if (stat(instance_manual_start_path, &instance_stat_buf) == 0 ||
                    stat(g_cmManualStartPath, &cluster_stat_buf) == 0 || !CheckStartDN()) {
                    dnStatus.reportMsg.local_status.db_state = INSTANCE_HA_STATE_MANUAL_STOPPED;
                } else if (g_dnDiskDamage[i]) {
                    dnStatus.reportMsg.local_status.db_state = INSTANCE_HA_STATE_DISK_DAMAGED;
                } else if (agentCheckPort(g_currentNode->datanode[i].datanodePort) > 0 ||
                           agentCheckPort(g_currentNode->datanode[i].datanodeLocalHAPort) > 0) {
                    dnStatus.reportMsg.local_status.db_state = INSTANCE_HA_STATE_PORT_USED;
                } else {
                    /*
                     * if instance is not running, cm_agent try to retsart it. if instance is still not running
                     * after MAX_INSTANCE_START times' trying, think it down.
                     */
                    if (g_dnStartCounts[i] > max_instance_start) {
                        char build_pid_path[MAXPGPATH];
                        int rcs = snprintf_s(build_pid_path, MAXPGPATH, MAXPGPATH - 1,
                            "%s/gs_build.pid",
                            g_currentNode->datanode[i].datanodeLocalDataPath);
                        securec_check_intval(rcs, (void)rcs);
                        pgpid_t pid = get_pgpid(build_pid_path, MAXPGPATH);
                        if ((pid > 0 && !is_process_alive(pid)) || pid < 0) {
                            dnStatus.reportMsg.local_status.db_state = INSTANCE_HA_STATE_BUILD_FAILED;
                        } else {
                            dnStatus.reportMsg.local_status.db_state = INSTANCE_HA_STATE_UNKONWN;
                        }
                    } else {
                        dnStatus.reportMsg.local_status.db_state = INSTANCE_HA_STATE_STARTING;
                    }
                }
                dnStatus.reportMsg.local_status.buildReason = INSTANCE_HA_DATANODE_BUILD_REASON_UNKNOWN;
            }
        }
        if (agent_backup_open == CLUSTER_STREAMING_STANDBY) {
            /* In streaming buackup cluster, role shoule change to primary and standby for arbitrate */
            ChangeLocalRoleInBackup(i, &dnStatus.reportMsg.local_status.local_role);
            if (dnStatus.reportMsg.local_status.local_role == INSTANCE_ROLE_PRIMARY &&
                dnStatus.reportMsg.local_status.db_state == INSTANCE_HA_STATE_NEED_REPAIR &&
                dnStatus.reportMsg.local_status.buildReason == INSTANCE_HA_DATANODE_BUILD_REASON_DISCONNECT) {
                ReportStreamingDRAlarm(ALM_AT_Fault, instanceName, alarmIndex, instanceName);
            } else {
                ReportStreamingDRAlarm(ALM_AT_Resume, instanceName, alarmIndex, NULL);
            }
        } else {
            ReportStreamingDRAlarm(ALM_AT_Resume, instanceName, alarmIndex, NULL);
        }
        if (g_clusterType != V3SingleInstCluster &&
            dnStatus.reportMsg.connectStatus == AGENT_TO_INSTANCE_CONNECTION_OK &&
            g_currentNode->datanode[i].datanodeRole != DUMMY_STANDBY_DN) {
            logicClusterName = get_logicClusterName_by_dnInstanceId(dnStatus.reportMsg.instanceId);
            if (strcmp(g_agentEnableDcf, "on") != 0) {
                if (dnStatus.reportMsg.local_status.local_role == INSTANCE_ROLE_PRIMARY) {
                    CheckAlmLocalPrimary(
                        &dnStatus, alarmIndex, (const char *)logicClusterName, (const char *)instanceName);
                } else if (dnStatus.reportMsg.local_status.local_role == INSTANCE_ROLE_STANDBY) {
                    CheckAlmLocalStandby(
                        &dnStatus, alarmIndex, (const char *)logicClusterName, (const char *)instanceName);
                }
            }
        }

        /* Number of times that dn is restarted within 10 minutes. */
        dnStatus.reportMsg.dn_restart_counts = g_primaryDnRestartCounts[i];
        if (dn_restart_count_check_time >= (DN_RESTART_COUNT_CHECK_TIME / agent_report_interval) ||
            dnStatus.reportMsg.local_status.local_role == INSTANCE_ROLE_STANDBY) {
            dn_restart_count_check_time = 0;
            dnStatus.reportMsg.dn_restart_counts = 0;
            g_primaryDnRestartCounts[i] = 0;
        }

        /* Number of times that dn is restarted within 1 hour. */
        dnStatus.reportMsg.dn_restart_counts_in_hour = g_primaryDnRestartCountsInHour[i];
        if (dn_restart_count_check_time_in_hour >= (DN_RESTART_COUNT_CHECK_TIME_HOUR / agent_report_interval) ||
            dnStatus.reportMsg.local_status.local_role == INSTANCE_ROLE_STANDBY) {
            dn_restart_count_check_time_in_hour = 0;
            dnStatus.reportMsg.dn_restart_counts_in_hour = 0;
            g_primaryDnRestartCountsInHour[i] = 0;
        }

        if (dnStatus.reportMsg.local_status.db_state == INSTANCE_HA_STATE_BUILD_FAILED) {
            report_build_fail_alarm(ALM_AT_Fault, instanceName, i);
        }
        if (dnStatus.reportMsg.local_status.db_state == INSTANCE_HA_STATE_NORMAL ||
            dnStatus.reportMsg.local_status.db_state == INSTANCE_HA_STATE_BUILDING) {
            report_build_fail_alarm(ALM_AT_Resume, instanceName, i);
        }

        write_runlog(DEBUG1, "DatanodeStatusCheck: local role is %d, db state is %d, build reason is %d\n",
            dnStatus.reportMsg.local_status.local_role, dnStatus.reportMsg.local_status.db_state,
            dnStatus.reportMsg.local_status.buildReason);
        DnCheckFloatIp(&dnStatus, (uint32)i, (bool8)(running == PROCESS_RUNNING));
        (void)pthread_rwlock_wrlock(&(g_dnReportMsg[i].lk_lock));
        rc = memcpy_s((void *)&(g_dnReportMsg[i].dnStatus.lpInfo), sizeof(AgentCmDnLocalPeer),
            (void *)&dnStatus.lpInfo, sizeof(AgentCmDnLocalPeer));
        securec_check_errno(rc, (void)rc);
        rc = memcpy_s((void *)&(g_dnReportMsg[i].dnStatus.reportMsg), sizeof(agent_to_cm_datanode_status_report),
            (void *)&dnStatus.reportMsg, sizeof(agent_to_cm_datanode_status_report));
        securec_check_errno(rc, (void)rc);
        rc = memcpy_s((void *)&(g_dnReportMsg[i].dnStatus.floatIpInfo), sizeof(CmaDnFloatIpInfo),
            (void *)&dnStatus.floatIpInfo, sizeof(CmaDnFloatIpInfo));
        securec_check_errno(rc, (void)rc);
        (void)pthread_rwlock_unlock(&(g_dnReportMsg[i].lk_lock));

        cm_sleep(agent_report_interval);
        check_dn_sql5_timer = (check_dn_sql5_timer > 0) ? (check_dn_sql5_timer - 1) : g_check_dn_sql5_interval;
        dn_restart_count_check_time++;
        dn_restart_count_check_time_in_hour++;
        UpdateThreadActivity(index);
    }
}

void* WRFloatIpCheckMain(void *arg)
{
    CmaWrFloatIp wrFloatIp;
    int32 i = *(int32*)arg;
    write_runlog(LOG, "WRFloatIpCheckMain: start to check wr float ip status.\n");

    for (;;) {
        if (g_shutdownRequest || !IsNeedCheckFloatIp() || (agent_backup_open != CLUSTER_PRIMARY) ||
            !g_enableWalRecord) {
            cm_sleep(SHUTDOWN_SLEEP_TIME);
            continue;
        }

        InitWrFloatIp(&wrFloatIp, g_nodeId);
        DnFloatIp *dnFloatIp = GetDnFloatIpByDnIdx(i);
        wrFloatIp.count = dnFloatIp->dnFloatIpCount;
        NetworkState state[MAX_FLOAT_IP_COUNT];
        GetFloatIpNicStatus(dnFloatIp->instId, CM_INSTANCE_TYPE_DN, state, MAX_FLOAT_IP_COUNT);
        for (uint32 k = 0; k < wrFloatIp.count; k++) {
            wrFloatIp.netState[k] = state[k];
        }
        ReportCmsWrFloatIp(&wrFloatIp, g_nodeId);
        NetworkOper oper = GetFloatIpOper(g_nodeId);
        SetNicOper(dnFloatIp->instId, CM_INSTANCE_TYPE_DN, NETWORK_TYPE_FLOATIP, oper);
        cm_sleep(agnet_report_wrFloatip_interval);
    }

}

/* kerberos status check */
uint32 check_kerberos_state(const char* username)
{
    /* commad: $GAUSSHOME/bin/kinit -k -t $KRB_HOME/kerberos/omm.keytab omm/opengauss.org@OPENGAUSS.ORG */
    if (check_one_instance_status("krb5kdc", "krb5kdc", NULL) == PROCESS_RUNNING) {
        char actualCmd[MAX_PATH_LEN] = {0};
        char kerberosCommandPath[MAX_PATH_LEN] = {0};
        pid_t status;
        int rcs = cmagent_getenv("KRB_HOME", kerberosCommandPath, sizeof(kerberosCommandPath));
        if (rcs != EOK) {
            write_runlog(LOG, "Get KRB_HOME failed, please check.\n");
            return KERBEROS_STATUS_UNKNOWN;
        } else {
            check_input_for_security(kerberosCommandPath);
            int32 ret = snprintf_s(actualCmd, MAX_PATH_LEN, MAX_PATH_LEN - 1,
                "%s/bin/kinit -k -t %s/kerberos/%s.keytab %s/opengauss.org@OPENGAUSS.ORG",
                kerberosCommandPath, kerberosCommandPath, username, username);
            securec_check_intval(ret, (void)ret);
            check_input_for_security(actualCmd);
            status = system(actualCmd);
            if (status == -1) {
                write_runlog(ERROR, "fail to execute command %s, and errno=%d.", actualCmd, errno);
                return KERBEROS_STATUS_UNKNOWN;
            } else {
                if (WIFEXITED(status)) {
                    if (WEXITSTATUS(status) == 0) {
                        return KERBEROS_STATUS_NORMAL;
                    } else {
                        return KERBEROS_STATUS_ABNORMAL;
                    }
                } else {
                    return KERBEROS_STATUS_UNKNOWN;
                }
            }
        }
    } else {
        return KERBEROS_STATUS_DOWN;
    }
}

/* get kerberos ip and port */
void get_kerberosConfigFile_info(const char* kerberosConfigFile, char* kerberosIp, uint32* kerberosPort, int* roleFlag)
{
    FILE* kerberos_config_fd;
    char buff[MAX_BUFF] = {0};
    char validstring[MAX_BUFF] = {0};
    errno_t rc;
    int rcs;
    char ip[CM_IP_LENGTH] = {0};
    int port = 0;
    if ((kerberos_config_fd = fopen(kerberosConfigFile, "re")) != NULL) {
        while (!feof(kerberos_config_fd)) {
            if (fgets(buff, MAX_BUFF, kerberos_config_fd) == NULL) {
                write_runlog(DEBUG1, "kerberos ip and port unkonw !\n");
                break;
            }
            if (strstr(buff, "kdc ") != NULL || strstr(buff, "kdc=") != NULL) {
                /* acquire kerberos ip and port */
                rcs = sscanf_s(buff, "%[^1-9]%[^:]%*c%d", validstring, MAX_BUFF, ip, CM_IP_LENGTH, &port);
                check_sscanf_s_result(rcs, 3);
                securec_check_intval(rcs, (void)fclose(kerberos_config_fd));
                if (strcmp(ip, g_currentNode->backIps[0]) == 0 && port != 0) {
                    rc = strncpy_s(kerberosIp, CM_IP_LENGTH, ip, strlen(ip));
                    securec_check_errno(rc, (void)fclose(kerberos_config_fd));
                    *kerberosPort = (uint32)port;
                    break;
                }
                *roleFlag = *roleFlag + 1;
            }
        }
        if (port == 0) {
            write_runlog(LOG, "Please reinstall kerberos!\n");
        }
        (void)fclose(kerberos_config_fd);
        return;
    } else {
        write_runlog(LOG, "kerberos config open error !\n");
        return;
    }
}
/* kerberos thread main funcation */
void* KerberosStatusCheckMain(void* arg)
{
    agent_to_cm_kerberos_status_report report_msg;
    errno_t rc;
    pthread_t threadId = pthread_self();
    set_thread_state(threadId);
    char kerberos_config_path[MAX_PATH_LEN] = {0};
    write_runlog(LOG, "kerberos status check thread start, threadid %lu.\n", threadId);
    char kerberosIp[CM_IP_LENGTH] = {0};
    uint32 kerberosPort = 0;
    int roleFlag = 0;

    rc = memset_s(
        &report_msg, sizeof(agent_to_cm_kerberos_status_report), 0, sizeof(agent_to_cm_kerberos_status_report));
    securec_check_errno(rc, (void)rc);
    report_msg.msg_type = MSG_AGENT_CM_KERBEROS_STATUS;
    int isKerberos = cmagent_getenv("MPPDB_KRB5_FILE_PATH", kerberos_config_path, sizeof(kerberos_config_path));
    if (isKerberos != EOK) {
        write_runlog(DEBUG1, "KerberosStatusCheckMain: MPPDB_KRB5_FILE_PATH get fail.\n");
        return NULL;
    }
    struct stat stat_buf = {0};
    check_input_for_security(kerberos_config_path);
    canonicalize_path(kerberos_config_path);
    if (stat(kerberos_config_path, &stat_buf) != 0) {
        write_runlog(DEBUG1, "KerberosStatusCheckMain: kerberos config file not exist.\n");
        return NULL;
    }
    /* get kerberos node and node_name */
    report_msg.node = g_currentNode->node;
    rc = strncpy_s(report_msg.nodeName, MAXLEN, g_currentNode->nodeName, strlen(g_currentNode->nodeName));
    securec_check_errno(rc, (void)rc);

    /* get kerberos ip and port and primary or standby */
    get_kerberosConfigFile_info(kerberos_config_path, kerberosIp, &kerberosPort, &roleFlag);
    rc = strncpy_s(report_msg.kerberos_ip, CM_IP_LENGTH, kerberosIp, strlen(kerberosIp));
    securec_check_errno(rc, (void)rc);

    report_msg.port = kerberosPort;
    if (roleFlag == 0) {
        rc = strncpy_s(report_msg.role, MAXLEN, "Primary", strlen("Primary"));
        securec_check_errno(rc, (void)rc);
    } else {
        rc = strncpy_s(report_msg.role, MAXLEN, "Standby", strlen("Standby"));
        securec_check_errno(rc, (void)rc);
    }

    for (;;) {
        if (g_shutdownRequest) {
            cm_sleep(5);
            continue;
        }

        /* get kerberos status */
        report_msg.status = check_kerberos_state(pw->pw_name);

        /* restart kerberos */
        int ret = 0;
        if (report_msg.status == KERBEROS_STATUS_ABNORMAL || report_msg.status == KERBEROS_STATUS_DOWN) {
            kill_instance_force("krb5kdc", INSTANCE_KERBEROS);
            cm_sleep(1);
            ret = system("krb5kdc");
            if (ret != 0) {
                write_runlog(ERROR, "run krb5kdc command failed and restart fail, errno=%d.!\n", errno);
            }
        }

        /* save report msg */
        (void)pthread_rwlock_wrlock(&(g_kerberosReportMsg.lk_lock));
        rc = memcpy_s((void*)&(g_kerberosReportMsg.report_msg),
            sizeof(agent_to_cm_kerberos_status_report),
            (void*)&report_msg,
            sizeof(agent_to_cm_kerberos_status_report));
        securec_check_errno(rc, (void)rc);
        (void)pthread_rwlock_unlock(&(g_kerberosReportMsg.lk_lock));
        cm_sleep(g_agentKerberosStatusCheckInterval);
    }
}


void CheckSharedDiskUsage(uint32 &vgdataPathUsage, uint32 &vglogPathUsage)
{
    FILE *fp;
    char result[1024];
    double percent1 = 0.0, percent2 = 0.0;

    fp = popen("dsscmd lsvg | awk 'NR==2 || NR==3 {print $NF}'", "r");
    if (fp == NULL) {
        write_runlog(ERROR, "Failed to exec command(dsscmd lsvg).\n");
        vgdataPathUsage = 0;
        vglogPathUsage = 0;
        return;
    }

    if (fgets(result, sizeof(result)-1, fp) != NULL) {
        sscanf(result, "%lf", &percent1);
    }
    if (fgets(result, sizeof(result)-1, fp) != NULL) {
        sscanf(result, "%lf", &percent2);
    }

    // If the value is greater than 0 and less than 1, consider it as 1.
    if (percent1 > 0 && percent1 < 1) {
        percent1 = 1;
    }
    if (percent2 > 0 && percent2 < 1) {
        percent2 = 1;
    }
    vgdataPathUsage = (uint)percent1;
    vglogPathUsage = (uint)percent2;

    pclose(fp);
}

/**
 * @brief Get DN node log path disk usage and datapath disk usage, send them to the CMS
 *
 */
void CheckDiskForDNDataPath()
{
    for (uint32 ii = 0; ii < g_currentNode->datanodeCount; ii++) {
        AgentToCmDiskUsageStatusReport status;
        status.msgType = (int)MSG_AGENT_CM_DISKUSAGE_STATUS;
        status.instanceId = g_currentNode->datanode[ii].datanodeId;
        status.logPathUsage = CheckDiskForLogPath();
        uint32 dataPathUsage = GetDiskUsageForPath(g_currentNode->datanode[ii].datanodeLocalDataPath);
        uint32 linkPathUsage = GetDiskUsageForLinkPath(g_currentNode->datanode[ii].datanodeLocalDataPath);
        status.dataPathUsage = (dataPathUsage > linkPathUsage) ? dataPathUsage : linkPathUsage;
        status.readOnly = g_dnReadOnly[ii];
        status.instanceType = INSTANCE_TYPE_DATANODE;
        if (IsCusResExistLocal()) {
            CheckSharedDiskUsage(status.vgdataPathUsage, status.vglogPathUsage);
            write_runlog(DEBUG1, "vgdataPathUsage:%u, vglogPathUsage:%u.\n",
                status.vgdataPathUsage, status.vglogPathUsage);
        } else {
            status.vgdataPathUsage = 0;
            status.vglogPathUsage = 0;
        }

        write_runlog(DEBUG1, "[%s] msgType:%d, instanceId:%u, logPathUsage:%u, linkPathUsage: %u, dataPathUsage:%u.\n",
            __FUNCTION__, status.msgType, status.instanceId, status.logPathUsage, linkPathUsage, status.dataPathUsage);

        (void)pthread_rwlock_wrlock(&(g_dnReportMsg[ii].lk_lock));
        errno_t rc = memcpy_s((void *)&(g_dnReportMsg[ii].dnStatus.diskUsageMsg),
            sizeof(AgentToCmDiskUsageStatusReport), (void *)&status, sizeof(AgentToCmDiskUsageStatusReport));
        securec_check_errno(rc, (void)rc);
        (void)pthread_rwlock_unlock(&(g_dnReportMsg[ii].lk_lock));
    }
}

/* 
 * This function is used to ensure whether the status modify;
 */
static int IsClusterInRedoState()
{
    FILE *fp;
    char buffer[1024] = {0};
    const char* expectedString = "in on-demand redo";
    char *foundString = NULL;

    fp = popen("pg_controldata +data | grep 'Cluster status:' | "
               "awk '{for (i=NF-2;i<=NF;i++) printf \"%s \", $i; printf \"\\n\"}'", "r");
    if (fp == NULL) {
        write_runlog(LOG, "Failed to exec command(pg_controldata +data).\n");
        return UNEXPECT_ONDEMAND_RECOVERY;
    }

    if (fgets(buffer, sizeof(buffer), fp) == NULL) {
        pclose(fp);
        return UNEXPECT_ONDEMAND_RECOVERY;
    }
    pclose(fp);

    foundString = buffer;

    char *end = foundString + strlen(foundString) - 1;
    while (end >= foundString && (*end == '\n' || *end == '\r' || *end == '\t' || *end == ' ')) {
        *end = '\0';
        end--;
    }
    /* If we found nothing, it must be something wrong with gaussdb or DSS. */
    if (*foundString == '\0') {
        return UNEXPECT_ONDEMAND_RECOVERY;
    }

    if (!strcmp(foundString, expectedString)) {
        return IN_ONDEMAND_RECOVERY;
    } else {
        return NOT_IN_ONDEMAND_RECOVERY;
    }
}


/* Report the in on-demand-status by msg, notify the CM server no need to wait switchover complated. */
static void AgentSendOnDemandStatus(int onDemandStatus)
{
    agent_to_cm_ondemand_status_report reportMsg = {0};
    reportMsg.msg_type = (int)MSG_AGENT_ONDEMAND_STATUES_REPORT;
    reportMsg.onDemandStatus = onDemandStatus;
    reportMsg.reportTime = time(NULL);
    reportMsg.nodeId = g_nodeHeader.node;
    PushMsgToCmsSendQue((char *)&reportMsg, (uint32)sizeof(agent_to_cm_ondemand_status_report), "ondemand status");
    write_runlog(DEBUG1, "Node(%u) ondemand status send to cms, status is: %d\n", reportMsg.nodeId, onDemandStatus);
}

/*
 * This function is main entry for pgdata charge.
 * It can be extended in the future.
 */
void PGDataControlCheck()
{
    int clusterStatusUnderOnDemand = IsClusterInRedoState();
    AgentSendOnDemandStatus(clusterStatusUnderOnDemand);
}

static void PingPeerIP(int* count, const char localIP[CM_IP_LENGTH], const char peerIP[CM_IP_LENGTH])
{
    char command[MAXPGPATH] = {0};
    char buf[MAXPGPATH];
    int rc;
    uint32 tryTimes = 3;

    const char *pingStr = GetPingStr(GetIpVersion(peerIP));
    rc = snprintf_s(command, MAXPGPATH, MAXPGPATH - 1,
        "%s -c 1 -w 1 -I %s %s > /dev/null;if [ $? == 0 ];then echo success;else echo fail;fi;",
        pingStr, localIP, peerIP);
    securec_check_intval(rc, (void)rc);
    write_runlog(DEBUG1, "ping command is: %s.\n", command);

    while (tryTimes > 0) {
        FILE* fp = popen(command, "r");
        if (fp == NULL) {
            write_runlog(ERROR, "popen failed\n.");
            return;
        }
        if (fgets(buf, sizeof(buf), fp) != NULL) {
            if (strstr(buf, "success") != NULL) {
                (*count)++;
                (void)pclose(fp);
                return;
            }
        }
        cm_sleep(1);
        tryTimes--;
        (void)pclose(fp);
    }
    write_runlog(ERROR, "ping peer ip failed: %s, command is: %s\n.", buf, command);
    return;
}

void GetPingSuccessCount(int i, int* count)
{
    if (g_multi_az_cluster) {
        for (uint32 j = 0; j < g_dn_replication_num - 1; ++j) {
            PingPeerIP(count, g_currentNode->datanode[i].datanodeLocalHAIP[0],
                       g_currentNode->datanode[i].peerDatanodes[j].datanodePeerHAIP[0]);
        }
    } else {
        PingPeerIP(count, g_currentNode->datanode[i].datanodeLocalHAIP[0],
                   g_currentNode->datanode[i].datanodePeerHAIP[0]);
        PingPeerIP(count, g_currentNode->datanode[i].datanodeLocalHAIP[0],
                   g_currentNode->datanode[i].datanodePeer2HAIP[0]);
    }
}

void CheckDNConnectionStatus(int i, int alarmIndex, const char *instanceName)
{
    AlarmType alarmType = ALM_AT_Resume;
    if (!g_mostAvailableSync[i]) {
        int count = 0;
        GetPingSuccessCount(i, &count);
        if (count == 0) {
            write_runlog(LOG, "dn(%u) is disconnected from other dn.\n", g_currentNode->datanode[i].datanodeId);
            g_dnPingFault[i] = true;
            if (g_dnReportMsg[i].dnStatus.reportMsg.local_status.local_role == INSTANCE_ROLE_PRIMARY  &&
                !g_isPauseArbitration) {
                if (g_enableWalRecord) {
                    for (uint32 i = 0; i < GetLocalResConfCount(); ++i) {
                        if (strcmp(g_resConf[i].resName, "gr") == 0) {
                            ManualStopLocalResInst(&g_resConf[i]);
                        }
                    }
                } else {
                    immediate_stop_one_instance(g_currentNode->datanode[i].datanodeLocalDataPath, INSTANCE_DN);
                }
            }
            alarmType = ALM_AT_Fault;
        } else {
            g_dnPingFault[i] = false;
        }
    } else {
        g_dnPingFault[i] = false;
    }
    ReportDNDisconnectAlarm(alarmType, instanceName, alarmIndex);
}

void* DNConnectionStatusCheckMain(void *arg)
{
    int i = *(int*)arg;
    int alarmIndex = i;
    char instanceName[CM_NODE_NAME] = {0};
    pthread_t threadId = pthread_self();

    if ((i < 0) || (i >= CM_MAX_DATANODE_PER_NODE)) {
        write_runlog(ERROR, "DN index [%d] is invalid, failed to start DNConnection thread.\n", i);
        return NULL;
    }
    if (g_single_node_cluster || IsBoolCmParamTrue(g_agentEnableDcf)) {
        write_runlog(LOG, "instanceId(%u) is single node cluster or in dcf mode, no need connection status check.\n",
                     g_currentNode->datanode[i].datanodeId);
        return NULL;
    }
    write_runlog(LOG, "dn(%d) connection status check thread start, threadid %lu.\n", i, threadId);
    int ret = snprintf_s(instanceName, sizeof(instanceName), sizeof(instanceName) - 1,
                         "%s_%u", "dn", g_currentNode->datanode[i].datanodeId);
    securec_check_intval(ret, (void)ret);
    for (;;) {
        set_thread_state(threadId);
        if (g_shutdownRequest || g_enableWalRecord) {
            cm_sleep(5);
            continue;
        }
        CheckDNConnectionStatus(i, alarmIndex, instanceName);
        cm_sleep(agent_report_interval);
    }
}

static bool IsDeviceNameSame(const char *device, int deviceCount, char * const *deviceName)
{
    for (int i = 0; i < deviceCount; ++i) {
        if (strcmp(device, deviceName[i]) == 0) {
            return true;
        }
    }

    return false;
}

static char **GetAllDisk(int &deviceCount)
{
    char **result;
    char tmpName[MAX_DEVICE_DIR] = {0};
    errno_t rc;
    size_t resultLen = (g_currentNode->datanodeCount + 1) * sizeof(char*);

    result = (char**)malloc(resultLen);
    if (result == NULL) {
        write_runlog(ERROR, "[CmReadfile] malloc failed, out of memory.\n");
        return NULL;
    }
    rc = memset_s(result, resultLen, 0, resultLen);
    securec_check_errno(rc, (void)rc);

    deviceCount = 0;
    if (g_currentNode->coordinate == 1) {
        CmGetDisk(g_currentNode->DataPath, tmpName, MAX_DEVICE_DIR);
        result[deviceCount] = strdup(tmpName);
        if (result[deviceCount] == NULL) {
            write_runlog(ERROR, "out of memory, deviceCount = %d\n", deviceCount);
            return NULL;
        }
        ++deviceCount;
        rc = memset_s(tmpName, MAX_DEVICE_DIR, 0, MAX_DEVICE_DIR);
        securec_check_errno(rc, (void)rc);
    }
    for (uint32 i = 0; i < g_currentNode->datanodeCount; ++i) {
        CmGetDisk(g_currentNode->datanode[i].datanodeLocalDataPath, tmpName, MAX_DEVICE_DIR);
        if (!IsDeviceNameSame(tmpName, deviceCount, result)) {
            result[deviceCount] = strdup(tmpName);
            if (result[deviceCount] == NULL) {
                write_runlog(ERROR, "out of memory, deviceCount = %d\n", deviceCount);
                return NULL;
            }
            ++deviceCount;
        }
        rc = memset_s(tmpName, MAX_DEVICE_DIR, 0, MAX_DEVICE_DIR);
        securec_check_errno(rc, (void)rc);
    }

    return result;
}

static bool IsSymbolRight(const char *str)
{
    int count = 0;

    for (int i = 0; str[i] != '\0'; ++i) {
        if (str[i] == ',') {
            ++count;
        }
    }

    if (count == THRESHOLD_FORMAT) {
        return true;
    }

    return false;
}

static bool IsValueRight(const char *value, int &param)
{
    if (value == NULL) {
        write_runlog(ERROR, "threshold value = NULL.\n");
        return false;
    }
    if (CM_is_str_all_digit(value) != 0) {
        write_runlog(ERROR, "threshold value = %s, is wrong.\n", value);
        return false;
    }
    param = (int)strtol(value, NULL, DECIMAL_NOTATION);
    if (param < THRESHOLD_MIN_VALUE || param > THRESHOLD_MAX_VALUE) {
        write_runlog(ERROR, "threshold value = %s, out of range.\n", value);
        return false;
    }

    return true;
}

static status_t GetThreshold(EnvThreshold &threshold)
{
    char *pLeft = NULL;
    char *pValue;
    char envStr[CM_PATH_LENGTH] = {0};

    if (strcmp(g_environmentThreshold, "") == 0) {
        write_runlog(DEBUG1, "environment_threshold is NULL.\n");
        return CM_ERROR;
    }
    errno_t rc = strcpy_s(envStr, CM_PATH_LENGTH, g_environmentThreshold);
    securec_check_errno(rc, (void)rc);
    char *tmp = trim(envStr);
    write_runlog(DEBUG1, "environment threshold, tmp=%s.\n", tmp);

    if (tmp[strlen(tmp) - 1] == ')') {
        tmp[strlen(tmp) - 1] = '\0';
    } else {
        write_runlog(ERROR, "line:%d, environment threshold format is wrong.\n", __LINE__);
        return CM_ERROR;
    }
    if (tmp[0] == '(') {
        tmp++;
    } else {
        write_runlog(ERROR, "line:%d, environment threshold format is wrong.\n", __LINE__);
        return CM_ERROR;
    }

    if (!IsSymbolRight(tmp)) {
        write_runlog(ERROR, "line:%d, environment threshold format is wrong.\n", __LINE__);
        return CM_ERROR;
    }

    pValue = strtok_r(tmp, ",", &pLeft);
    if (!IsValueRight(pValue, threshold.mem)) {
        return CM_ERROR;
    }
    pValue = strtok_r(NULL, ",", &pLeft);
    if (!IsValueRight(pValue, threshold.cpu)) {
        return CM_ERROR;
    }
    pValue = strtok_r(NULL, ",", &pLeft);
    if (!IsValueRight(pValue, threshold.disk)) {
        return CM_ERROR;
    }
    pValue = strtok_r(NULL, ",", &pLeft);
    if (!IsValueRight(pValue, threshold.instMem)) {
        return CM_ERROR;
    }
    if (!IsValueRight(pLeft, threshold.instPool)) {
        return CM_ERROR;
    }

    return CM_SUCCESS;
}

static int CheckMemoryHave()
{
    FILE *fp;
    uint64 memFree = 0;
    uint64 memTotal = 0;
    int ret;
    bool haveGetFree = false;
    bool haveGetTotal = false;
    char line[CM_PATH_LENGTH] = {0};

    if ((fp = fopen(FILE_MEMINFO, "re")) == NULL) {
        write_runlog(ERROR, "failed to open file %s.\n", FILE_MEMINFO);
        return -1;
    }

    while (fgets(line, CM_PATH_LENGTH, fp) != NULL) {
        if (strncmp(line, "MemTotal", strlen("MemTotal")) == 0) {
            ret = sscanf_s(line, "MemTotal: %lu kB\n", &memTotal);
            check_sscanf_s_result(ret, 1);
            securec_check_intval(ret, (void)ret);
            write_runlog(DEBUG1, "CheckMemoryHave memTotal = %lu.\n", memTotal);
            haveGetTotal = true;
        }
        if (strncmp(line, "MemFree", strlen("MemFree")) == 0) {
            ret = sscanf_s(line, "MemFree: %lu kB", &memFree);
            check_sscanf_s_result(ret, 1);
            securec_check_intval(ret, (void)ret);
            write_runlog(DEBUG1, "CheckMemoryHave memTotal = %lu.\n", memFree);
            haveGetFree = true;
        }
        if (haveGetTotal && haveGetFree) {
            break;
        }
    }
    (void)fclose(fp);

    if (memTotal == 0) {
        write_runlog(ERROR, "get memTotal(%lu) info is 0.\n", memTotal);
        return -1;
    }

    return (int)(PERCENT - (memFree * PERCENT / memTotal));
}

static int CheckCpuHave()
{
    return ReadCpuStatus(1, NULL, true);
}

static void CheckDiskIoHave(const char *deviceName, int disk)
{
    int cpuNum;
    int diskIoHave;
    static IoStat ioStatus = {0};

    if (deviceName == NULL) {
        write_runlog(LOG, "device name is NULL, can't check its disk IO.\n");
        return;
    }
    cpuNum = GetCpuCount();
    diskIoHave = (int)ReadDiskIOStat(deviceName, cpuNum, &ioStatus, false);
    if (diskIoHave > disk) {
        write_runlog(LOG, "{\"CMA disk IO is more than threshold\":"
            "{\"disk IO\":{\"name\":\"%s\",\"actual\":\"%d%%\", \"threshold\":\"%d%%\"}}}\n",
            deviceName, diskIoHave, disk);
    }

    return;
}

static void CheckSysStatus(const EnvThreshold &threshold)
{
    int memHave = 0;
    int cpuHave = 0;
    bool isOverflow;

    if (threshold.mem == 0 && threshold.cpu == 0) {
        write_runlog(DEBUG5, "threshold mem and cpu is 0, not need do check.\n");
        return;
    }

    if (threshold.mem != 0) {
        if ((memHave = CheckMemoryHave()) < 0) {
            write_runlog(ERROR, "get memory info fail.\n");
            return;
        }
    }
    if (threshold.cpu != 0) {
        if ((cpuHave = CheckCpuHave()) < 0) {
            write_runlog(ERROR, "get cpu info fail.\n");
            return;
        }
    }

    isOverflow = (memHave > threshold.mem) || (cpuHave > threshold.cpu);
    if (isOverflow) {
        write_runlog(LOG, "{\"CMA physical resource is more than threshold\":"
            "{\"memory\":{\"actual\":\"%d%%\",\"threshold\":\"%d%%\"},"
            "\"CPU\":{\"actual\":\"%d%%\",\"threshold\":\"%d%%\"}}}\n",
            memHave, threshold.mem, cpuHave, threshold.cpu);
    }

    return;
}

static void CheckDiskStatus(const EnvThreshold &threshold, const char * const *deviceName, int deviceCount)
{
    if (threshold.disk == 0) {
        write_runlog(DEBUG5, "threshold disk is 0, not need do check.\n");
        return;
    }

    for (int i = 0; i < deviceCount; ++i) {
        CheckDiskIoHave(deviceName[i], threshold.disk);
    }

    return;
}

void *CheckNodeStatusThreadMain(void * const arg)
{
    int deviceCount = 0;
    long expiredTime;
    struct timeval checkEnd;
    struct timeval checkBegin;
    EnvThreshold threshold = {0};
    char **deviceName = GetAllDisk(deviceCount);
    if (deviceName == NULL) {
        write_runlog(ERROR, "CheckNodeStatusThreadMain, out of memory.\n");
        return NULL;
    }

    write_runlog(LOG, "CMA deviceCount = %d.\n", deviceCount);
    for (;;) {
        if (g_shutdownRequest) {
            cm_sleep(SHUTDOWN_SLEEP_TIME);
            continue;
        }

        (void)gettimeofday(&checkBegin, NULL);
        if (GetThreshold(threshold) != CM_SUCCESS) {
            threshold = {0, 0, 0, 0, 0};
        }
        CheckSysStatus(threshold);
        CheckDiskStatus(threshold, deviceName, deviceCount);
#ifdef ENABLE_MULTIPLE_NODES
        CheckAllInstStatus(&threshold);
#endif
        (void)gettimeofday(&checkEnd, NULL);

        expiredTime = (checkEnd.tv_sec - checkBegin.tv_sec);
        write_runlog(DEBUG5, "CheckNodeStatusThreadMain take %ld seconds.\n", expiredTime);

        if (expiredTime < CHECK_INTERVAL) {
            cm_sleep((unsigned int)(CHECK_INTERVAL - expiredTime));
        }
    }

    return NULL;
}

int CreateCheckNodeStatusThread()
{
    int err;
    pthread_t thrId;

    if ((err = pthread_create(&thrId, NULL, CheckNodeStatusThreadMain, NULL)) != 0) {
        write_runlog(ERROR, "Failed to create new thread: error %d.\n", err);
        return err;
    }
    return 0;
}

void InitSystemStatInfo(SystemStatInfo* systemStat)
{
    errno_t rc = memset_s(systemStat, sizeof(SystemStatInfo), 0, sizeof(SystemStatInfo));
    securec_check_errno(rc, (void)rc);
    systemStat->memoryStatInfo.lastReportTime = GetMonotonicTimeS();
    systemStat->cpuStatInfo.lastReportTime = GetMonotonicTimeS();
    int diskCount = 0;
    char **diskName = GetAllDisk(diskCount);
    if (diskName == NULL || diskCount <= 0) {
        write_runlog(ERROR, "Get disk info failed.\n");
        return;
    }
    systemStat->diskStatInfo.diskCount = diskCount;
    systemStat->diskStatInfo.disIoStatInfo = new DisIoStatInfo[diskCount];
    if (systemStat->diskStatInfo.disIoStatInfo == NULL) {
        write_runlog(ERROR, "InitSystemStatInfo: out of memory, diskCount = %d\n", diskCount);
        for (int i = 0; i < diskCount; ++i) {
            free(diskName[i]);
        }
        free(diskName);
        return;
    }
    for (int i = 0; i < diskCount; ++i) {
        if (CM_IS_EMPTY_STR(diskName[i])) {
            continue;
        }
        rc = strcpy_s(systemStat->diskStatInfo.disIoStatInfo[i].diskName, MAX_DEVICE_DIR, diskName[i]);
        securec_check_errno(rc, (void)rc);
        systemStat->diskStatInfo.disIoStatInfo[i].lastReportTime = GetMonotonicTimeS();
        systemStat->diskStatInfo.disIoStatInfo[i].ioUtil = 0.0f;
        systemStat->diskStatInfo.disIoStatInfo[i].svctm = 0.0f;
        systemStat->diskStatInfo.disIoStatInfo[i].lastReportIoUtil = 0.0f;
        systemStat->diskStatInfo.disIoStatInfo[i].totalWeight = 0;
        systemStat->diskStatInfo.disIoStatInfo[i].lastReadCount = 0;
        systemStat->diskStatInfo.disIoStatInfo[i].lastWriteCount = 0;
        systemStat->diskStatInfo.disIoStatInfo[i].lastCheckTime = 0;
        systemStat->diskStatInfo.disIoStatInfo[i].lastIoTime = 0;
        systemStat->diskStatInfo.disIoStatInfo[i].weightWindow = std::deque<uint32>();
        write_runlog(LOG, "start check device(%s).\n", diskName[i]);
    }
    for (int i = 0; i < diskCount; ++i) {
        free(diskName[i]);
    }
    free(diskName);
}

status_t SetMemeryStatInfo(const char* line, MemoryStatInfo* memoryStatInfo, uint32& count)
{
    if (CM_IS_EMPTY_STR(line)) {
        return CM_ERROR;
    }
    uint32 length = ELEMENT_COUNT(g_memoryCheckInfoList);
    for (uint32 i = 0; i < length; i++) {
        MemCheckInfo memCheckInfo = g_memoryCheckInfoList[i];
        if (strncmp(line, memCheckInfo.name, strlen(memCheckInfo.name)) != 0) {
            continue;
        }
        int32 ret = sscanf_s(line, memCheckInfo.info, &(memoryStatInfo->memItemList[memCheckInfo.item]));
        check_sscanf_s_result(ret, 1);
        if (ret == -1) {
            write_runlog(ERROR, "failed to sscanf %s, line=[%s].\n", memCheckInfo.info, line);
            return CM_ERROR;
        }
        ++count;
        return CM_SUCCESS;
    }
    return CM_SUCCESS;
}

status_t CheckMemoryStatus(MemoryStatInfo* memoryStatInfo)
{
    char line[CM_PATH_LENGTH] = {0};
    uint32 success = 0;
    FILE* fp = fopen(FILE_MEMINFO, "re");
    if (fp == NULL) {
        write_runlog(ERROR, "failed to open file %s.\n", FILE_MEMINFO);
        return CM_ERROR;
    }
    while (fgets(line, CM_PATH_LENGTH, fp)!= NULL) {
        if (SetMemeryStatInfo(line, memoryStatInfo, success) != CM_SUCCESS) {
            FCLOSE_AND_RESET(fp);
            return CM_ERROR;
        }
        if (success == (uint32)MEM_STAT_BUTT) {
            break;
        }
    }
    fclose(fp);

    uint64 sysMemUsed = memoryStatInfo->memItemList[MEM_STAT_TOTAL] - memoryStatInfo->memItemList[MEM_STAT_FREE]-
            memoryStatInfo->memItemList[MEM_STAT_BUFFERS] - memoryStatInfo->memItemList[MEM_STAT_CACHED];
    uint64 appMemUsed = memoryStatInfo->memItemList[MEM_STAT_TOTAL] - memoryStatInfo->memItemList[MEM_STAT_AVAILABLE];

    if (sysMemUsed > memoryStatInfo->memItemList[MEM_STAT_TOTAL]
            || appMemUsed > memoryStatInfo->memItemList[MEM_STAT_TOTAL]) {
        write_runlog(ERROR, "check memory status failed, memTotal(%lu), memSys(%lu), memApp(%lu).\n",
                     memoryStatInfo->memItemList[MEM_STAT_TOTAL], sysMemUsed, appMemUsed);
        return CM_ERROR;
    }
    memoryStatInfo->systemMemUsedUtil = (float)sysMemUsed /
        (float)memoryStatInfo->memItemList[MEM_STAT_TOTAL] * PERCENT;
    memoryStatInfo->appMemUsedUtil = (float)appMemUsed / (float)memoryStatInfo->memItemList[MEM_STAT_TOTAL] * PERCENT;
    return CM_SUCCESS;
}

void ReportSystemStatusAlarm(const SystemStatInfo* systemStat, const EnvThreshold* threshold)
{
    int memUsed = (int)systemStat->memoryStatInfo.systemMemUsedUtil;
    int appMemUsed = (int)systemStat->memoryStatInfo.appMemUsedUtil;
    if (threshold->mem > 0 && (memUsed > threshold->mem || appMemUsed > threshold->mem)) {
        write_runlog(LOG, "system memory usage is %u, app memory usage is %u, threshold is %u, report alarm.\n",
            memUsed, appMemUsed, threshold->mem);
        ReportMemoryAbnormalAlarm(memUsed, appMemUsed, threshold->mem);
    }
    int cpuUsed = (int)systemStat->cpuStatInfo.cpuUtil;
    if (threshold->cpu > 0 && cpuUsed > threshold->cpu) {
        write_runlog(LOG, "system cpu usage is %u, threshold is %u, report alarm.\n", cpuUsed, threshold->cpu);
        ReportCpuAbnormalAlarm(cpuUsed, threshold->cpu);
    }
    for (uint32 i = 0; i < systemStat->diskStatInfo.diskCount; ++i) {
        DisIoStatInfo* disIoStatInfo = &(systemStat->diskStatInfo.disIoStatInfo[i]);
        if (CM_IS_EMPTY_STR(disIoStatInfo->diskName)) {
            continue;
        }
        int ioUtil = (int)disIoStatInfo->ioUtil;
        if (threshold->disk > 0 && ioUtil > threshold->disk) {
            write_runlog(LOG, "system disk(%s) io usage is %u, threshold is %u, report alarm.\n",
                         disIoStatInfo->diskName,
                         ioUtil,
                         threshold->disk);
            ReportDiskIOAbnormalAlarm(disIoStatInfo->diskName, ioUtil, threshold->disk);
        }
        float svctm = disIoStatInfo->svctm;
        for (int j = SVCTM_LEVEL_CEIL - 1; j >= 0; --j) {
            if (svctm >= g_slowIoLevelInfoList[j].threshold) {
                disIoStatInfo->weightWindow.push_back(g_slowIoLevelInfoList[j].weight);
                disIoStatInfo->totalWeight += g_slowIoLevelInfoList[j].weight;
                break;
            }
        }
        AlarmType alarmType = ALM_AT_Resume;
        if (disIoStatInfo->weightWindow.size() > SLOW_DISK_CHECK_PERIOD) {
            disIoStatInfo->totalWeight -= disIoStatInfo->weightWindow.front();
            disIoStatInfo->weightWindow.pop_front();
            char details[MAX_PATH_LEN] = {0};
            if (disIoStatInfo->totalWeight > SLOW_DISK_CHECK_THRESHOLD) {
                alarmType = ALM_AT_Fault;
                int ret = snprintf_s(details, MAX_PATH_LEN, MAX_PATH_LEN - 1, "weight: %lu, period: %lu, "
                    "threshold: %lu", disIoStatInfo->totalWeight, SLOW_DISK_CHECK_PERIOD, SLOW_DISK_CHECK_THRESHOLD);
                securec_check_intval(ret, (void)ret);
                write_runlog(LOG, "detected slow disk(%s), details: %s, report alarm.\n",
                             disIoStatInfo->diskName,
                             details);
            }
            ReportSlowDiskAlarm(disIoStatInfo->diskName, alarmType, i, details);
        }
    }
}

status_t SetCpuStatInfo(const char* line, CpuSimpleInfo* cpuSimpleInfo)
{
    const int cpuNameLen = 8;
    const int cpuInfoCount = 11;
    char cpuName[cpuNameLen] = {0};
    CpuInfo* tmpCpuInfo = &(cpuSimpleInfo->cpuInfo);
    errno_t rc = sscanf_s(line, "%s %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu", cpuName, cpuNameLen,
        &tmpCpuInfo->cpuUser, &tmpCpuInfo->cpuNice, &tmpCpuInfo->cpuSys, &tmpCpuInfo->cpuIdle, &tmpCpuInfo->cpuIwait,
        &tmpCpuInfo->cpuHardirq, &tmpCpuInfo->cpuSoftirq, &tmpCpuInfo->cpuSteal, &tmpCpuInfo->cpuGuest,
        &tmpCpuInfo->cpuGuestNice);
    check_sscanf_s_result(rc, cpuInfoCount);
    if (rc != cpuInfoCount) {
        write_runlog(ERROR, "get cpu info from %s failed, line %s, count %d.\n", FILE_CPUSTAT, line, rc);
        return CM_ERROR;
    }

    cpuSimpleInfo->cpuIdleTime = tmpCpuInfo->cpuIdle;
    cpuSimpleInfo->cpuUserTime = tmpCpuInfo->cpuUser;
    cpuSimpleInfo->cpuSystemTime = tmpCpuInfo->cpuSys;
    cpuSimpleInfo->cpuTotalTime = 0;
    uint64* value = (uint64*)tmpCpuInfo;
    uint32 len = (uint32)sizeof(CpuInfo) / sizeof(uint64);
    for (uint32 i = 0; i < len; ++i) {
        cpuSimpleInfo->cpuTotalTime += *(value++);
    }
    return (strcmp(cpuName, "cpu") == 0) ? CM_SUCCESS : CM_ERROR;
}

status_t CheckCpuStatus(CpuStatInfo* cpuStatInfo)
{
    CpuSimpleInfo curCpuInfo = {0};
    char line[MAX_PATH_LEN] = {0};
    FILE* fp = fopen(FILE_CPUSTAT, "re");
    if (fp == NULL) {
        write_runlog(ERROR, "failed to open file %s.\n", FILE_CPUSTAT);
        return CM_ERROR;
    }
    while (fgets(line, sizeof(line), fp)!= NULL) {
        if (SetCpuStatInfo(line, &curCpuInfo)!= CM_SUCCESS) {
            write_runlog(ERROR, "failed to set cpu stat info, line=[%s].\n", line);
            FCLOSE_AND_RESET(fp);
            return CM_ERROR;
        }
        break;
    }
    fclose(fp);
    if (cpuStatInfo->oldCpuInfo.cpuTotalTime != 0) {
        uint64* oldValue = (uint64*) &(cpuStatInfo->oldCpuInfo);
        uint64* curValue = (uint64*) &(curCpuInfo);
        uint32 count = (uint32)sizeof(CpuSimpleInfo) / sizeof(uint64);
        for (uint32 i = 0; i < count; ++i) {
            if (*(curValue++) < *(oldValue++)) {
                write_runlog(ERROR, "get cpu info wrong, curCpuTime(%lu) < oldCpuTime(%lu). and index=[%u]\n",
                    *(curValue--), *(oldValue--), i);
                errno_t rc = memcpy_s(&(cpuStatInfo->oldCpuInfo), sizeof(CpuSimpleInfo), &(curCpuInfo),
                    sizeof(CpuSimpleInfo));
                securec_check_errno(rc, (void)  rc);
                return CM_ERROR;
            }
        }
        uint64 totalTimeInterval = curCpuInfo.cpuTotalTime - cpuStatInfo->oldCpuInfo.cpuTotalTime;
        uint64 sysTimeInterval = curCpuInfo.cpuSystemTime - cpuStatInfo->oldCpuInfo.cpuSystemTime;
        uint64 userTimeInterval = curCpuInfo.cpuUserTime - cpuStatInfo->oldCpuInfo.cpuUserTime;
        uint64 idleTimeInterval = curCpuInfo.cpuIdleTime - cpuStatInfo->oldCpuInfo.cpuIdleTime;
        cpuStatInfo->cpuUtil = (float)(totalTimeInterval - idleTimeInterval) / (float)totalTimeInterval * PERCENT;
        cpuStatInfo->cpuSystemUtil = (float)sysTimeInterval / (float)totalTimeInterval * PERCENT;
        cpuStatInfo->cpuUserUtil = (float)userTimeInterval / (float)totalTimeInterval * PERCENT;
    }
    errno_t rc = memcpy_s(&(cpuStatInfo->oldCpuInfo), sizeof(CpuSimpleInfo), &(curCpuInfo), sizeof(CpuSimpleInfo));
    securec_check_errno(rc, (void)  rc);
    return CM_SUCCESS;
}

void CalculateDiskIoUtil(DisIoStatInfo* disIoStatInfo, uint64 curTime, uint64 ioTimeMs,
                         uint64 readCount, uint64 writeCount)
{
    uint64 rwCount = (readCount + writeCount) - (disIoStatInfo->lastReadCount + disIoStatInfo->lastWriteCount);
    rwCount = (rwCount == 0) ? 1 : rwCount;
    if ((disIoStatInfo->lastCheckTime == 0) || (disIoStatInfo->lastCheckTime >= curTime)
        || (disIoStatInfo->lastIoTime >= ioTimeMs) || (rwCount < 0)) {
        if (disIoStatInfo->lastCheckTime >= curTime) {
            write_runlog(ERROR, "lastCheckTime(%lu) >= curTime(%lu), get disk io info failed.\n",
                disIoStatInfo->lastCheckTime, curTime);
        }
        if (disIoStatInfo->lastIoTime > ioTimeMs) {
            write_runlog(ERROR, "lastIoTime(%lu) > ioTimeMs(%lu), get disk io info failed.\n",
                         disIoStatInfo->lastIoTime, ioTimeMs);
        }
        if (rwCount < 0) {
            write_runlog(ERROR, "lastReadCount(%lu) + lastWriteCount(%lu) > readCount(%lu) + writeCount(%lu),"
                " get disk io info failed.\n", disIoStatInfo->lastReadCount,
                disIoStatInfo->lastWriteCount, readCount, writeCount);
        }
        disIoStatInfo->lastCheckTime = curTime;
        disIoStatInfo->lastIoTime = ioTimeMs;
        disIoStatInfo->lastReadCount = readCount;
        disIoStatInfo->lastWriteCount = writeCount;
        disIoStatInfo->ioUtil = 0;
        return;
    }
    uint64 ioTimeInterval = ioTimeMs - disIoStatInfo->lastIoTime;
    uint64 timeInterval = curTime - disIoStatInfo->lastCheckTime;
    if (ioTimeInterval > timeInterval) {
        ioTimeInterval = timeInterval;
    }
    disIoStatInfo->ioUtil = (float)ioTimeInterval / (float)timeInterval * PERCENT;
    disIoStatInfo->svctm = (float)ioTimeInterval / (float)rwCount;
    disIoStatInfo->lastCheckTime = curTime;
    disIoStatInfo->lastIoTime = ioTimeMs;
    disIoStatInfo->lastReadCount = readCount;
    disIoStatInfo->lastWriteCount = writeCount;
}

void SetOneDiskStatus(DisIoStatInfo* disIoStatInfo)
{
    FILE* fp = fopen(FILE_DISKSTAT, "re");
    if (fp == NULL) {
        write_runlog(ERROR, "failed to open file %s.\n", FILE_DISKSTAT);
        return;
    }
    uint32 majorDeviceNum;
    uint32 minorDeviceNum;
    char deviceName[MAX_DEVICE_DIR] = {0};
    uint64 readCount;
    uint64 readMerge;
    uint64 readSector;
    uint64 readTimeMs;
    uint64 writeCount;
    uint64 writeMerge;
    uint64 writeSector;
    uint64 writeTimeMs;
    uint64 ioCountInProgress;
    uint64 ioTimeMs;
    uint64 ioWeightTimeMs;
    int cnt;
    char line[MAX_PATH_LEN] = {0};
    uint64 curTime;
    while (fgets(line, sizeof(line), fp) != NULL) {
        curTime = GetMonotonicTimeMs();
        cnt = sscanf_s(line, "%u %u %s %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu",
            &majorDeviceNum, &minorDeviceNum, deviceName, MAX_DEVICE_DIR - 1, &readCount, &readMerge, &readSector,
            &readTimeMs, &writeCount, &writeMerge, &writeSector, &writeTimeMs,
            &ioCountInProgress, &ioTimeMs, &ioWeightTimeMs);
        check_sscanf_s_result(cnt, MAX_DEVICE_STAT_INDEX);
        if (cnt != MAX_DEVICE_STAT_INDEX) {
            write_runlog(ERROR, "get disk info from %s failed, line %s, count %d.\n", FILE_DISKSTAT, line, cnt);
            continue;
        }
        if (strcmp(disIoStatInfo->diskName, deviceName) == 0) {
            CalculateDiskIoUtil(disIoStatInfo, curTime, ioTimeMs, readCount, writeCount);
            fclose(fp);
            return;
        }
    }
    fclose(fp);
}

status_t CheckDiskStatus(DiskStatInfo* diskStatInfo)
{
    if (diskStatInfo->diskCount == 0) {
        write_runlog(DEBUG5, "diskCount is 0, can not check disk io.\n");
        return CM_ERROR;
    }
    for (uint32 i = 0; i < diskStatInfo->diskCount; ++i) {
        if (CM_IS_EMPTY_STR(diskStatInfo->disIoStatInfo[i].diskName)) {
            write_runlog(LOG, "diskName is empty, can not check disk io.\n");
            continue;
        }
        SetOneDiskStatus(&diskStatInfo->disIoStatInfo[i]);
    }
    return CM_SUCCESS;
}

void CheckDnDiskUsage()
{
    if (g_diskUsageThreshold == 0) {
        return;
    }
    for (uint32 i = 0; i < g_currentNode->datanodeCount; ++i) {
        uint32 diskUsage = GetDiskUsageForPath(g_currentNode->datanode[i].datanodeLocalDataPath);
        if (diskUsage > g_diskUsageThreshold) {
            char deviceName[MAX_PATH_LEN] = {0};
            CmGetDisk(g_currentNode->datanode[i].datanodeLocalDataPath, deviceName, MAX_PATH_LEN);
            write_runlog(LOG, "name %s, disk usage is %u, threshold is %u, report alarm.\n",
                         deviceName, diskUsage, g_diskUsageThreshold);
            ReportDiskUsageAbnormalAlarm(deviceName, diskUsage, g_diskUsageThreshold);
        }
    }
}

void CheckDnDiskDamageAlarm()
{
    for (uint32 i = 0; i < g_currentNode->datanodeCount; ++i) {
        CheckDnDiskDamage(i);
    }
}

void* CheckSysStatusThreadMain(void* const arg)
{
    pthread_t threadId = pthread_self();
    write_runlog(LOG, "system status check thread start, threadid %lu.\n", threadId);

    uint64 lastReportSysTime = GetMonotonicTimeS();
    EnvThreshold threshold = {0};
    SystemStatInfo systemStat;
    errno_t rc = memset_s(&systemStat, sizeof(SystemStatInfo), 0, sizeof(SystemStatInfo));
    securec_check_errno(rc, (void)rc);
    InitSystemStatInfo(&systemStat);

    for (;;) {
        if (g_shutdownRequest) {
            cm_sleep(SHUTDOWN_SLEEP_TIME);
            break;
        }
        uint64 curTime = GetMonotonicTimeS();
        if ((g_sys_report_interval != 0) && (curTime - lastReportSysTime >= g_sys_report_interval)) {
            CheckMemoryStatus(&systemStat.memoryStatInfo);
            CheckCpuStatus(&systemStat.cpuStatInfo);
            CheckDiskStatus(&systemStat.diskStatInfo);
            lastReportSysTime = curTime;
        }
        if (GetThreshold(threshold)!= CM_SUCCESS) {
            threshold = {0, 0, 0, 0, 0};
        }
        CheckDnDiskUsage();
        CheckDnDiskDamageAlarm();
        ReportSystemStatusAlarm(&systemStat, &threshold);
        cm_sleep(1);
    }
    delete[] systemStat.diskStatInfo.disIoStatInfo;
    write_runlog(LOG, "system status check thread exit.\n");
    return NULL;
}

int CreateCheckSysStatusThread()
{
    int err;
    pthread_t thrId;

    if ((err = pthread_create(&thrId, NULL, CheckSysStatusThreadMain, NULL)) != 0) {
        write_runlog(ERROR, "Failed to create new thread: error %d.\n", err);
        return err;
    }
    return 0;
}

static DDB_ROLE GetCurrentEtcdRole()
{
    const uint32 serverLen = 2;
    ServerSocket server[serverLen] = {{0}};
    SetServerSocketWithEtcdInfo(&server[0], g_currentNode);
    server[1].host = NULL;
    DdbInitConfig config = {DB_ETCD};
    GetDdbCfgApi(&config.drvApiInfo, server, serverLen);
    DdbNodeState nodeState;
    DdbConn dbCon = {0};
    status_t st = InitDdbConn(&dbCon, &config);
    if (st != CM_SUCCESS) {
        write_runlog(ERROR, "etcd open failed when query etcd status. %s\n", DdbGetLastError(&dbCon));
        return DDB_ROLE_UNKNOWN;
    }
    st = DdbInstanceState(&dbCon, g_currentNode->etcdName, &nodeState);
    if (DdbFreeConn(&dbCon) != CM_SUCCESS) {
        write_runlog(WARNING, "etcd_close failed,%s\n", DdbGetLastError(&dbCon));
    }
    if (st != CM_SUCCESS) {
        write_runlog(ERROR, "[GetCurrentEtcdRole] failed ,error is %s\n", DdbGetLastError(&dbCon));
        return DDB_ROLE_UNKNOWN;
    }
    return nodeState.role;
}

static void StopCurrentETCD(void)
{
    char command[MAXPGPATH];
    int ret = snprintf_s(command, MAXPGPATH, MAXPGPATH - 1, "echo -e %s > %s; chmod 600 %s",
        CM_AGENT_BIN_NAME, g_cmEtcdManualStartPath, g_cmEtcdManualStartPath);
    securec_check_intval(ret, (void)ret);
    ret = system(command);
    if (ret != 0) {
        write_runlog(ERROR, "Failed to stop the etcd node with executing the command: command=\"%s\","
            " nodeId=%u, systemReturn=%d, shellReturn=%d, errno=%d.\n",
            command, g_currentNode->node, ret, SHELL_RETURN_CODE(ret), errno);
    }
}

static void StartCurrentETCD(void)
{
    char command[MAXPGPATH];
    int ret = snprintf_s(command, MAXPGPATH, MAXPGPATH - 1, "rm -f %s", g_cmEtcdManualStartPath);
    securec_check_intval(ret, (void)ret);
    ret = system(command);
    if (ret != 0) {
        write_runlog(ERROR, "Failed to start the etcd node with executing the command: command=\"%s\","
            " nodeId=%u, systemReturn=%d, shellReturn=%d, errno=%d.\n",
            command, g_currentNode->node, ret, SHELL_RETURN_CODE(ret), errno);
    }
}

static bool IsEtcdStopByCmAgent(const char* path)
{
    char stopType[MAX_PATH_LEN] = {0};
    char realPath[PATH_MAX] = {0};
    if (realpath(path, realPath) == NULL) {
        write_runlog(DEBUG1, "Canonical etcd_manual_start file failed errno=%d.\n", errno);
        return false;
    }
    FILE* fd = fopen(realPath, "re");
    if (fd == NULL) {
        write_runlog(ERROR, "Open etcd_manual_start failed \n");
        return false;
    }
    if (fscanf_s(fd, "%s", stopType, sizeof(stopType)) != 1) {
        write_runlog(ERROR, "invalid data in etcd_manual_start file \"%s\"\n", path);
        (void)fclose(fd);
        return false;
    }
    (void)fclose(fd);
    if (strcmp(stopType, CM_AGENT_BIN_NAME) == 0) {
        return true;
    }
    return false;
}

void* ETCDConnectionStatusCheckMain(void* arg)
{
    pthread_t threadId = pthread_self();
    write_runlog(LOG, "etcd connection status check thread start, threadid %lu.\n", threadId);
    int count;
    DDB_ROLE etcdRole = DDB_ROLE_UNKNOWN;
    for (;;) {
        set_thread_state(threadId);
        if (g_shutdownRequest || g_exitFlag) {
            cm_sleep(5);
            continue;
        }
        count = 0;
        for (uint32 i = 0; i < g_node_num; ++i) {
            if (!g_node[i].etcd || g_currentNode->etcdId == g_node[i].etcdId) {
                continue;
            }
            PingPeerIP(&count, g_currentNode->etcdClientListenIPs[0], g_node[i].etcdClientListenIPs[0]);
        }
        DDB_ROLE tmpRole = GetCurrentEtcdRole();
        etcdRole = tmpRole == DDB_ROLE_UNKNOWN ? etcdRole : tmpRole;
        if (count == 0) {
            write_runlog(WARNING, "current etcd is disconnected from other, etcd num=%u.\n", g_etcd_num);
            if (access(g_cmEtcdManualStartPath, F_OK) != 0 && etcdRole == DDB_ROLE_LEADER) {
                write_runlog(WARNING, "current etcd is leader, cmagent need stop it for etcd availability.\n");
                StopCurrentETCD();
            }
        } else {
            if (IsEtcdStopByCmAgent(g_cmEtcdManualStartPath)) {
                write_runlog(LOG, "current etcd is stop by cmagent and connection is normal, need start it.\n");
                StartCurrentETCD();
            }
        }
        cm_sleep(agent_report_interval);
    }
    return NULL;
}

#ifdef ENABLE_XALARMD
static void TrimTrailingBlanks(char *str)
{
    if (str == NULL) {
        return;
    }
    int32 len = (int32)strlen(str);
    while (len > 0 && isspace((unsigned char)str[len - 1])) {
        str[len - 1] = '\0';
        --len;
    }
}

static char *SkipLeadingBlanks(char *str)
{
    if (str == NULL) {
        return NULL;
    }
    while (*str != '\0' && isspace((unsigned char)*str)) {
        ++str;
    }
    return str;
}

static bool ParseNodeMapEntry(char *entry, XalarmNodeMapItem *mapItem)
{
    char *entryStart = SkipLeadingBlanks(entry);
    if (entryStart == NULL || *entryStart == '\0') {
        return false;
    }
    TrimTrailingBlanks(entryStart);

    char *firstSep = strchr(entryStart, ':');
    if (firstSep == NULL) {
        return false;
    }
    *firstSep = '\0';
    char *nodeIdText = SkipLeadingBlanks(entryStart);
    char *cnaText = SkipLeadingBlanks(firstSep + 1);
    TrimTrailingBlanks(nodeIdText);
    TrimTrailingBlanks(cnaText);
    if (nodeIdText == NULL || cnaText == NULL || nodeIdText[0] == '\0' || cnaText[0] == '\0') {
        return false;
    }

    mapItem->nodeId = (uint32)strtoul(nodeIdText, NULL, DECIMAL_BASE);
    mapItem->cna = (uint32)strtoul(cnaText, NULL, DECIMAL_BASE);
    return (mapItem->nodeId != 0 && mapItem->cna != 0);
}

static bool LoadXalarmNodeMap()
{
    g_xalarmNodeMapCount = 0;
    char mapText[XALARM_EVENT_MAP_TEXT_LEN] = {0};
    if (get_config_param(configDir, "xalarm_node_map", mapText, sizeof(mapText)) < 0) {
        write_runlog(LOG, "xalarm_node_map is not configured, xalarm event will not be mapped.\n");
        return false;
    }

    char *savePtr = NULL;
    char *entry = strtok_r(mapText, ";", &savePtr);
    while (entry != NULL && g_xalarmNodeMapCount < XALARM_EVENT_NODE_MAP_MAX) {
        XalarmNodeMapItem mapItem = {0};
        if (ParseNodeMapEntry(entry, &mapItem)) {
            g_xalarmNodeMap[g_xalarmNodeMapCount++] = mapItem;
        } else {
            write_runlog(ERROR, "invalid xalarm_node_map entry: %s.\n", entry);
        }
        entry = strtok_r(NULL, ";", &savePtr);
    }

    write_runlog(LOG, "loaded %u xalarm map items from cm_agent.conf.\n", g_xalarmNodeMapCount);
    return (g_xalarmNodeMapCount > 0);
}

static uint32 FindAlarmNodeByCna(uint32 cna)
{
    for (uint32 i = 0; i < g_xalarmNodeMapCount; ++i) {
        if (g_xalarmNodeMap[i].cna == cna) {
            return g_xalarmNodeMap[i].nodeId;
        }
    }
    return INVALID_ALARM_NODE_ID;
}

static bool ParseRemoteNodeAlarmDesc(const char *alarmDesc, uint32 *cna)
{
    const char *payload = strstr(alarmDesc, "{cna:");
    if (payload == NULL) {
        return false;
    }
    int ret = sscanf_s(payload, "{cna:%u", cna);
    return (ret == 1);
}

static bool IsNeedProcessXalarmEvent(int alarmId)
{
    return (alarmId == (int)XALARM_PANIC_EVENT_ID || alarmId == (int)XALARM_KERNEL_REBOOT_EVENT_ID);
}

/* Map original xalarm event id to paired ACK id for xalarm_report_event; 0 means no ACK for this id. */
static uint32 XalarmMapAlarmIdToAckEventId(int alarmId)
{
    switch (alarmId) {
        case (int)XALARM_REBOOT_EVENT_ID:
            return XALARM_REBOOT_ACK_EVENT_ID;
        case (int)XALARM_OOM_EVENT_ID:
            return XALARM_OOM_ACK_EVENT_ID;
        case (int)XALARM_PANIC_EVENT_ID:
            return XALARM_PANIC_ACK_EVENT_ID;
        case (int)XALARM_KERNEL_REBOOT_EVENT_ID:
            return XALARM_KERNEL_REBOOT_ACK_EVENT_ID;
        default:
            return 0;
    }
}

static void ReportXalarmEventAck(int alarmId, const char *alarmDesc)
{
    uint32 ackId = XalarmMapAlarmIdToAckEventId(alarmId);
    if (ackId == 0) {
        return;
    }

    char ackBuf[8192] = {0};
    errno_t rc;
    if (alarmDesc != NULL && alarmDesc[0] != '\0') {
        rc = strncpy_s(ackBuf, sizeof(ackBuf), alarmDesc, sizeof(ackBuf) - 1);
        securec_check_errno(rc, (void)rc);
    } else {
        rc = snprintf_s(ackBuf, sizeof(ackBuf), sizeof(ackBuf) - 1, "{\"ack\":\"cm_agent\",\"alarm_id\":%d}", alarmId);
        securec_check_intval(rc, (void)rc);
    }

    /* New libxalarm: len must equal strlen(pucParas) and <= 8191. Legacy: two-parameter API. */
#ifdef CM_XALARM_REPORT_EVENT_HAS_LEN
    size_t payloadLen = strlen(ackBuf);
    int rptRet = xalarm_report_event((unsigned short)ackId, ackBuf, payloadLen);
#else
    int rptRet = xalarm_report_event((unsigned short)ackId, ackBuf);
#endif
    if (rptRet != 0) {
        write_runlog(WARNING, "xalarm_report_event ack failed, ret=%d errno=%d ackId=%u srcAlarmId=%d.\n",
            rptRet, errno, ackId, alarmId);
    } else {
        write_runlog(DEBUG1, "xalarm_report_event ack ok, ackId=%u srcAlarmId=%u.\n", ackId, (uint32)alarmId);
    }
}

static bool WaitCmsPrimarySwitchByConnect(uint32 alarmNodeId, uint32 timeoutMs)
{
    const uint32 retryIntervalMs = 500;
    uint32 waitedMs = 0;
    while (waitedMs < timeoutMs) {
        if (g_shutdownRequest || g_exitFlag) {
            return false;
        }
        CM_Conn *primaryConn = GetConnToCmserver(0);
        if (primaryConn != NULL) {
            uint32 primaryNodeId = g_serverNodeId;
            CMPQfinish(primaryConn);
            if (primaryNodeId != alarmNodeId) {
                write_runlog(LOG,
                    "confirm cms primary switched by connect check, alarmNodeId=%u, currentPrimaryNodeId=%u.\n",
                    alarmNodeId, primaryNodeId);
                return true;
            }
            write_runlog(DEBUG1,
                "cms primary still on alarm node, alarmNodeId=%u, currentPrimaryNodeId=%u, waited=%u ms.\n",
                alarmNodeId, primaryNodeId, waitedMs);
        }
        CmUsleep(retryIntervalMs * MICROSECONDS_PER_MILLISECOND);
        waitedMs += retryIntervalMs;
    }
    write_runlog(WARNING, "wait cms primary switch by connect timeout, alarmNodeId=%u, waited %u ms.\n",
        alarmNodeId, timeoutMs);
    return false;
}

static void SendXalarmEventToLocalCms(const AgentToCmPanicRebootAlarmReport *alarmMsg)
{
    if (g_currentNode == NULL || g_currentNode->cmServerLevel != 1) {
        return;
    }

    CM_Conn *localConn = GetConnToLocalCmserver();
    if (localConn == NULL) {
        write_runlog(WARNING, "xalarm event cannot connect local cms, nodeId=%u, alarmId=%u.\n",
            g_currentNode->node, alarmMsg->alarmId);
        return;
    }

    AgentToCmPanicRebootAlarmReport localAlarmMsg = *alarmMsg;
    localAlarmMsg.msgType = MSG_AGENT_CM_PANIC_REBOOT_ALARM;
    if (SendMsgToCmsByConn(localConn, (const char *)&localAlarmMsg, (uint32)sizeof(localAlarmMsg)) != CM_SUCCESS) {
        write_runlog(ERROR, "xalarm event report to local cms failed, nodeId=%u, alarmId=%u.\n",
            g_currentNode->node, alarmMsg->alarmId);
    } else {
        write_runlog(LOG, "xalarm event reported to local cms first, nodeId=%u, alarmId=%u.\n",
            g_currentNode->node, alarmMsg->alarmId);
    }
    CMPQfinish(localConn);
}

static bool SendXalarmEventToPrimaryCms(const AgentToCmPanicRebootAlarmReport *alarmMsg)
{
    const uint32 retryCount = 10;
    const uint32 retryIntervalMs = 500;
    for (uint32 i = 0; i < retryCount; ++i) {
        if (g_shutdownRequest || g_exitFlag) {
            return false;
        }
        CM_Conn *primaryConn = GetConnToCmserver(0);
        if (primaryConn != NULL) {
            AgentToCmPanicRebootAlarmReport primaryAlarmMsg = *alarmMsg;
            primaryAlarmMsg.msgType = MSG_AGENT_CM_PANIC_REBOOT_ALARM_TO_PRIMARY;
            bool sendOk = (SendMsgToCmsByConn(
                primaryConn, (const char *)&primaryAlarmMsg, (uint32)sizeof(primaryAlarmMsg)) == CM_SUCCESS);
            uint32 primaryNodeId = g_serverNodeId;
            CMPQfinish(primaryConn);
            if (sendOk) {
                write_runlog(LOG, "xalarm event reported to primary cms, primaryNodeId=%u, alarmId=%u.\n",
                    primaryNodeId, alarmMsg->alarmId);
                return true;
            }
            write_runlog(ERROR, "xalarm event report to primary cms failed, retry=%u, alarmId=%u.\n",
                i + 1, alarmMsg->alarmId);
        }
        CmUsleep(retryIntervalMs * MICROSECONDS_PER_MILLISECOND);
    }
    return false;
}

static void ProcessXalarmEventReport(struct alarm_info *param, int alarmId)
{
    char *alarmDesc = xalarm_getdesc(param);
    if (alarmDesc == NULL) {
        write_runlog(ERROR, "xalarm desc is null, alarmId=%d.\n", alarmId);
        return;
    }
    ReportXalarmEventAck(alarmId, alarmDesc);

    uint32 cna = 0;
    if (!ParseRemoteNodeAlarmDesc(alarmDesc, &cna)) {
        write_runlog(ERROR, "failed to parse xalarm payload, alarmId=%d, payload=%s.\n", alarmId, alarmDesc);
        return;
    }

    uint32 alarmNodeId = FindAlarmNodeByCna(cna);
    if (alarmNodeId == INVALID_ALARM_NODE_ID) {
        write_runlog(ERROR, "cannot find node mapping for xalarm, alarmId=%d, cna=%u.\n", alarmId, cna);
        return;
    }

    AgentToCmPanicRebootAlarmReport alarmMsg = {0};
    alarmMsg.msgType = MSG_AGENT_CM_PANIC_REBOOT_ALARM;
    alarmMsg.sourceNodeId = g_currentNode->node;
    alarmMsg.alarmNodeId = alarmNodeId;
    alarmMsg.cna = cna;
    alarmMsg.alarmId = (uint32)alarmId;
    alarmMsg.alarmType = (uint32)xalarm_gettype(param);
    alarmMsg.alarmTime = (int64)xalarm_gettime(param);
    alarmMsg.eid[0] = '\0';
    errno_t rc = strncpy_s(alarmMsg.alarmDesc, sizeof(alarmMsg.alarmDesc), alarmDesc, sizeof(alarmMsg.alarmDesc) - 1);
    securec_check_errno(rc, (void)rc);

    SendXalarmEventToLocalCms(&alarmMsg);
    if (!WaitCmsPrimarySwitchByConnect(alarmNodeId, CMS_PRIMARY_SWITCH_WAIT_TIMEOUT_MS)) {
        write_runlog(ERROR, "xalarm event wait cms primary switch timeout, alarmId=%d, nodeId=%u.\n",
            alarmId, alarmNodeId);
        return;
    }
    write_runlog(LOG, "send xalarm event to primary cms, alarmId=%d, nodeId=%u, cna=%u.\n",
        alarmId, alarmNodeId, cna);
    if (!SendXalarmEventToPrimaryCms(&alarmMsg)) {
        write_runlog(ERROR, "report xalarm event to primary cms failed after retries, alarmId=%d, nodeId=%u.\n",
            alarmId, alarmNodeId);
        return;
    }
    write_runlog(LOG, "reported xalarm event to cms done, alarmId=%d, nodeId=%u, cna=%u.\n",
        alarmId, alarmNodeId, cna);
}

static void HandleXalarmEvent(struct alarm_info *param)
{
    if (g_shutdownRequest || g_exitFlag) {
        return;
    }
    int alarmId = xalarm_getid(param);
    if (IsNeedProcessXalarmEvent(alarmId)) {
        write_runlog(LOG, "receive xalarm panic/kernel reboot event, alarmId=%d, alarmType=%d, alarmTime=%lld.\n",
            alarmId, xalarm_gettype(param), (long long)xalarm_gettime(param));
        ProcessXalarmEventReport(param, alarmId);
        return;
    }
    switch (alarmId) {
        case (int)XALARM_REBOOT_EVENT_ID:
        case (int)XALARM_OOM_EVENT_ID:
            write_runlog(DEBUG1, "receive reserved xalarm event, alarmId=%d, alarmType=%d.\n",
                alarmId, xalarm_gettype(param));
            ReportXalarmEventAck(alarmId, xalarm_getdesc(param));
            break;
        case (int)XALARM_UBUS_MEM_EVENT_ID:
        case (int)XALARM_REBOOT_ACK_EVENT_ID:
        case (int)XALARM_OOM_ACK_EVENT_ID:
        case (int)XALARM_PANIC_ACK_EVENT_ID:
        case (int)XALARM_KERNEL_REBOOT_ACK_EVENT_ID:
            write_runlog(DEBUG1, "receive reserved xalarm event, alarmId=%d, alarmType=%d.\n",
                alarmId, xalarm_gettype(param));
            break;
        default:
            write_runlog(DEBUG1, "receive unsupported xalarm event, alarmId=%d.\n", alarmId);
            break;
    }
}

static void *XalarmEventCheckMain(void *arg)
{
    (void)arg;
    int regRet = -1;
    struct alarm_register *reg = NULL;
    write_runlog(LOG, "xalarm event check thread start.\n");
    struct alarm_subscription_info idFilter = {0};
    uint32 filterIdx = 0;
    idFilter.id_list[filterIdx++] = (int)XALARM_PANIC_EVENT_ID;
    idFilter.id_list[filterIdx++] = (int)XALARM_KERNEL_REBOOT_EVENT_ID;
    idFilter.len = (int)filterIdx;

    while (!g_shutdownRequest && !g_exitFlag) {
        if (regRet < 0) {
            regRet = xalarm_register_event(&reg, idFilter);
            if (regRet < 0) {
                write_runlog(ERROR, "xalarm_register_event (panic/reboot events) failed, ret=%d.\n", regRet);
                cm_sleep(SHUTDOWN_SLEEP_TIME);
                continue;
            }
            write_runlog(LOG, "xalarm event register success (event API).\n");
        }
        g_xalarmEventRegister = reg;

        struct alarm_msg msg;
        while (!g_shutdownRequest && !g_exitFlag) {
            errno_t rc = memset_s(&msg, sizeof(msg), 0, sizeof(msg));
            securec_check_errno(rc, (void)rc);
            int getRet = xalarm_get_event(&msg, reg);
            if (getRet < 0) {
                write_runlog(WARNING, "xalarm_get_event (panic/reboot) failed, ret=%d errno=%d.\n", getRet, errno);
                break;
            }
            struct alarm_info info;
            CmaXalarmMsgToAlarmInfo(&msg, &info);
            HandleXalarmEvent(&info);
        }
        if (g_xalarmEventRegister == reg && reg != NULL) {
            CmaXalarmUnregisterEvent(&reg);
            g_xalarmEventRegister = NULL;
        }
        if (g_shutdownRequest || g_exitFlag) {
            break;
        }
        cm_sleep(1);
    }
    write_runlog(LOG, "xalarm event check thread exit.\n");
    return NULL;
}

void CreateXalarmEventCheckThread(void)
{
    char enableParam[BOOL_STR_MAX_LEN] = {0};
    if (get_config_param(configDir, "enable_xalarm_event_check", enableParam, sizeof(enableParam)) < 0) {
        if (get_config_param(configDir, "enable_xalarm_panic_reboot_check", enableParam, sizeof(enableParam)) < 0) {
            write_runlog(LOG, "enable_xalarm_event_check is not configured, xalarm event thread is skipped.\n");
            return;
        }
    }
    if (!CheckBoolConfigParam(enableParam)) {
        write_runlog(ERROR, "invalid enable_xalarm_event_check value: %s,"
            "xalarm event thread is skipped.\n", enableParam);
        return;
    }
    if (!IsBoolCmParamTrue(enableParam)) {
        write_runlog(LOG, "xalarm event check disabled by config.\n");
        return;
    }
    if (!LoadXalarmNodeMap()) {
        write_runlog(ERROR, "xalarm event check enabled, but xalarm_node_map is invalid.\n");
        return;
    }

    pthread_t threadId;
    if (pthread_create(&threadId, NULL, XalarmEventCheckMain, NULL) != 0) {
        write_runlog(ERROR, "failed to create xalarm event check thread.\n");
        return;
    }
}

static AlarmType GetAlarmType(int alarmtype)
{
    if (alarmtype == XALARM_TYPE_OCCUR) {
        return ALM_AT_Fault;
    } else if (alarmtype == XALARM_TYPE_RECOVER) {
        return ALM_AT_Resume;
    }
    write_runlog(ERROR, "Unknown alarm type: %d\n", alarmtype);
    return ALM_AT_Fault;
}

static void BuildAlarmDetails(char *details, size_t maxSize, AlarmType alarmType,
                              const char *driverName, const char *dataPath)
{
    errno_t rc;
    if (alarmType == ALM_AT_Fault) {
        rc = snprintf_s(details, maxSize, maxSize - 1,
            "detected slow disk by xalarm plugin, data path: %s",
            dataPath);
        securec_check_intval(rc, (void)rc);
    } else {
        write_runlog(ERROR, "receive recover alarm type: %d\n", alarmType);
        rc = snprintf_s(details, maxSize, maxSize - 1,
            "recovered by xalarm plugin, data path: %s",
            dataPath);
        securec_check_intval(rc, (void)rc);
    }
}

static void HandlePrimarySlowDisk(const char *dataPath, int index)
{
    write_runlog(LOG, "Checking datanode role: index=%d, role=%d, path=%s\n",
        index, g_dnReportMsg[index].dnStatus.reportMsg.local_status.local_role, dataPath);
    if (g_dnReportMsg[index].dnStatus.reportMsg.local_status.local_role == INSTANCE_ROLE_PRIMARY) {
        immediate_stop_one_instance(dataPath, INSTANCE_DN);
    } else {
        write_runlog(LOG, "Slow disk detected on non-primary node, no restart needed: role=%d, path=%s\n",
            g_dnReportMsg[index].dnStatus.reportMsg.local_status.local_role, dataPath);
    }
}

static bool IsDeviceNameMatching(const char* deviceName, const char* driverName)
{
    if (strlen(deviceName) <= 0 || strlen(driverName) <= 0) {
        return false;
    }
    if (strncmp(deviceName, driverName, strlen(driverName)) != 0) {
        return false;
    }
    size_t driverNameLen = strlen(driverName);
    char nextChar = deviceName[driverNameLen];
    return (nextChar == '\0' || (nextChar == 'p' && isdigit(deviceName[driverNameLen + 1])));
}

static bool IsValidAlarmType(int alarmtype)
{
    return (alarmtype == XALARM_TYPE_OCCUR || alarmtype == XALARM_TYPE_RECOVER);
}

static void ProcessMatchedDevice(const char *driverName, int alarmtype, int index, const char *dataPath)
{
    if (!IsValidAlarmType(alarmtype)) {
        return;
    }
    
    AlarmType alarmType = GetAlarmType(alarmtype);
    char details[MAX_PATH_LEN] = {0};
    BuildAlarmDetails(details, MAX_PATH_LEN, alarmType, driverName, dataPath);
    
    if (alarmType == ALM_AT_Fault) {
        HandlePrimarySlowDisk(dataPath, index);
    } else {
        write_runlog(LOG, "Slow disk recovered: %s, data path: %s\n", driverName, dataPath);
    }
    
    ReportSlowDiskAlarm(driverName, alarmType, index, details);
}

void HandleXalarm(struct alarm_info *param)
{
    if (g_shutdownRequest || g_exitFlag) {
        return;
    }
    
    int alarmid = xalarm_getid(param);
    int alarmlevel = xalarm_getlevel(param);
    int alarmtype = xalarm_gettype(param);
    long long alarmtime = xalarm_gettime(param);
    char *alarmStr = xalarm_getdesc(param);
    
    write_runlog(LOG, "received xalarm notification: [alarmid:%d] "
        "[alarmlevel:%d] [alarmtype:%d] [alarmtime:%lld ms]\n", alarmid, alarmlevel, alarmtype, alarmtime);

    cJSON *alarm_info = cJSON_Parse(alarmStr);
    if (alarm_info == NULL) {
        write_runlog(ERROR, "cannot parse alarm info from xalarm.\n");
        return;
    }
    
    if (!cJSON_IsObject(alarm_info)) {
        write_runlog(ERROR, "alarm_info not found or not an object\n");
        cJSON_Delete(alarm_info);
        return;
    }
    
    cJSON *driver_name = cJSON_GetObjectItem(alarm_info, "driver_name");
    if (!cJSON_IsString(driver_name) || (driver_name->valuestring == NULL)) {
        write_runlog(ERROR, "driver_name not found or not a string.\n");
        cJSON_Delete(alarm_info);
        return;
    }

    for (int i = 0; i < (int)g_currentNode->datanodeCount; ++i) {
        char deviceName[MAX_PATH_LEN] = {0};
        const char *dataPath = g_currentNode->datanode[i].datanodeLocalDataPath;
        CmGetDisk(dataPath, deviceName, MAX_PATH_LEN);
        
        write_runlog(LOG, "CmGetDisk: path=%s, deviceName=%s, driver_name=%s, datanodeCount=%d\n",
                     dataPath, deviceName, driver_name->valuestring, (int)g_currentNode->datanodeCount);
        
        if (IsDeviceNameMatching(deviceName, driver_name->valuestring)) {
            ProcessMatchedDevice(driver_name->valuestring, alarmtype, i, dataPath);
            break;
        }
    }
    
    cJSON_Delete(alarm_info);
}
#endif