/*
 * Copyright (c) 2025 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_disk_check.cpp
 *
 *
 * IDENTIFICATION
 *    src/cm_agent/cma_disk_check.cpp
 *
 * -------------------------------------------------------------------------
 */

#include "cm/cm_util.h"
#include "cm/cm_msg.h"
#include "cma_status_check.h"
#include "cma_global_params.h"
#include "cma_disk_check.h"
#include "alarm/alarm.h"
#ifdef ENABLE_XALARMD
#ifdef __cplusplus
extern "C" {
#endif
#include <xalarm/register_xalarm.h>
#ifdef __cplusplus
}
#endif
#include "cjson/cJSON.h"
int g_xalarmClientId = -1;
static const uint64 XALARM_SLOW_DISK_ID = 1002;
#endif

static uint32 g_diskCheckTimeout = DISK_CHECK_TIMEOUT_DEFAULT;
static uint32 g_diskCheckInterval = DISK_CHECK_INTERVAL_DEFAULT;
static uint32 g_diskCheckBufferSize = DISK_CHECK_BUFFER_SIZE_DEFAULT;
static char* g_diskCheckBuffer = NULL;
static pthread_rwlock_t g_diskCheckBufferLock = PTHREAD_RWLOCK_INITIALIZER;

static DiskHealth g_diskHealth = {0};

void LoadDiskCheckConfig(const char *configFile)
{
    g_diskCheckTimeout = get_uint32_value_from_config(configFile, "disk_check_timeout", DISK_CHECK_TIMEOUT_DEFAULT);
    g_diskCheckInterval = get_uint32_value_from_config(configFile, "disk_check_interval", DISK_CHECK_INTERVAL_DEFAULT);
    g_diskCheckBufferSize = get_uint32_value_from_config(configFile, "disk_check_buffer_size",
                                                         DISK_CHECK_BUFFER_SIZE_DEFAULT);
    char enableXalarmStr[MAXPGPATH] = {0};
    if (get_config_param(configFile, "enable_xalarmd_slow_disk_check", enableXalarmStr, sizeof(enableXalarmStr)) >= 0) {
        g_enableXalarmdFeature = IsBoolCmParamTrue(enableXalarmStr);
        write_runlog(LOG, "enable_xalarmd_slow_disk_check is set to %s\n", g_enableXalarmdFeature ? "true" : "false");
    }
    pthread_rwlock_wrlock(&g_diskCheckBufferLock);
    FREE_AND_RESET(g_diskCheckBuffer);
    if (g_diskCheckBufferSize <= 0) {
        write_runlog(ERROR, "Invalid disk check buffer size %d, use default size %d", g_diskCheckBufferSize,
                     DISK_CHECK_BUFFER_SIZE_DEFAULT);
        g_diskCheckBufferSize = DISK_CHECK_BUFFER_SIZE_DEFAULT;
    }
    g_diskCheckBuffer = (char *) malloc(sizeof(char) * g_diskCheckBufferSize);
    if (g_diskCheckBuffer == NULL) {
        write_runlog(ERROR, "Out of memory, use default check buffer size %d", DISK_CHECK_BUFFER_SIZE_DEFAULT);
        g_diskCheckBufferSize = DISK_CHECK_BUFFER_SIZE_DEFAULT;
        g_diskCheckBuffer = (char *) CmMalloc(sizeof(char) * g_diskCheckBufferSize);
    } else {
        errno_t rc = memset_s(g_diskCheckBuffer, g_diskCheckBufferSize, 0, g_diskCheckBufferSize);
        securec_check_errno(rc, (void)rc);
    }
    pthread_rwlock_unlock(&g_diskCheckBufferLock);
}

bool IsDiskNameSame(const char *diskName, uint32 diskCount, char* const* diskNames)
{
    for (uint32 i = 0; i < diskCount; ++i) {
        if (strcmp(diskName, diskNames[i]) == 0) {
            return true;
        }
    }
    return false;
}

bool IsDiskHasDir(const char *dirPath, const char* diskName)
{
    char name[MAX_DEVICE_DIR] = {0};
    GetDiskNameByDataPath(dirPath, name, MAX_DEVICE_DIR);
    if (name[0] == '\0') {
        return false;
    }
    return strcmp(name, diskName) == 0;
}

uint32 GetDirCountByDisk(const char *diskName)
{
    uint32 dirCount = 0;
    if (IsDiskHasDir(g_logBasePath, diskName)) {
        dirCount++;
    }
    if (IsDiskHasDir(g_currentNode->cmDataPath, diskName)) {
        dirCount++;
    }
    for (uint32 i = 0; i < g_currentNode->datanodeCount; i++) {
        if (IsDiskHasDir(g_currentNode->datanode[i].datanodeLocalDataPath, diskName)) {
            dirCount++;
        }
    }
    return dirCount;
}

status_t GetDiskNameByPath(const char *diskPath, uint32* diskCount, char** diskNames)
{
    char tmpName[MAX_DEVICE_DIR] = {0};
    GetDiskNameByDataPath(diskPath, tmpName, MAX_DEVICE_DIR);
    if (tmpName[0] == '\0') {
        write_runlog(ERROR, "Failed to get disk name by path %s\n", diskPath);
        return CM_ERROR;
    }
    if (!IsDiskNameSame(tmpName, *diskCount, diskNames)) {
        diskNames[*diskCount] = strdup(tmpName);
        if (diskNames[*diskCount] == NULL) {
            write_runlog(ERROR, "Out of memory, get path(%s) disk name failed.\n", diskPath);
            return CM_ERROR;
        }
        ++(*diskCount);
    }
    return CM_SUCCESS;
}

char** GetAllDiskNames(uint32 *diskCount)
{
    // log and cmData
    const uint32 other = 2;
    (*diskCount) = 0;
    char** diskNames = (char**) CmMalloc(sizeof(char *) * (g_currentNode->datanodeCount + other));
    for (uint32 i = 0; i < g_currentNode->datanodeCount; i++) {
        if (GetDiskNameByPath(g_currentNode->datanode[i].datanodeLocalDataPath, diskCount, diskNames) != CM_SUCCESS) {
            FreePtr2Ptr(diskNames, (*diskCount));
            (*diskCount) = 0;
            return NULL;
        }
    }
    if (GetDiskNameByPath(g_logBasePath, diskCount, diskNames)!= CM_SUCCESS) {
        FreePtr2Ptr(diskNames, (*diskCount));
        (*diskCount) = 0;
        return NULL;
    }
    if (GetDiskNameByPath(g_currentNode->cmDataPath, diskCount, diskNames)!= CM_SUCCESS) {
        FreePtr2Ptr(diskNames, (*diskCount));
        (*diskCount) = 0;
        return NULL;
    }
    return diskNames;
}

void InitDirAlarmItem(DirHealth* dirHealth)
{
    for (uint32 i = 0; i < dirHealth->dirCount; ++i) {
        Alarm* diskAlarm = dirHealth->dir[i].diskAlarm;
        AlarmItemInitialize(&(diskAlarm[DISK_ALARM_READ_WRITE_SLOW]), ALM_AI_DiskReadWriteSlow, ALM_AS_Init, NULL);
        AlarmItemInitialize(&(diskAlarm[DISK_ALARM_HUNG]), ALM_AI_DiskHang, ALM_AS_Init, NULL);
    }
}

status_t InitDirHealthCtx(const char* diskName, DirHealth* dirHealth)
{
    uint32 dirCount = GetDirCountByDisk(diskName);
    dirHealth->dirCount = dirCount;
    if (dirCount == 0) {
        write_runlog(ERROR, "No dir in disk(%s).\n", diskName);
        return CM_ERROR;
    }
    dirHealth->dir = (DirHealthItem*)malloc(sizeof(DirHealthItem) * dirCount);
    if (dirHealth->dir == NULL) {
        write_runlog(ERROR, "[%s] Out of memory, get disk(%s) dir info failed.\n", __FUNCTION__, diskName);
        return CM_ERROR;
    }
    errno_t rc = memset_s(dirHealth->dir, sizeof(DirHealthItem) * dirCount, 0, sizeof(DirHealthItem) * dirCount);
    securec_check_errno(rc, (void)rc);

    uint32 index = 0;
    for (uint32 i = 0; i < g_currentNode->datanodeCount; i++) {
        const char* dnDataPath = g_currentNode->datanode[i].datanodeLocalDataPath;
        if (IsDiskHasDir(dnDataPath, diskName)) {
            dirHealth->dir[index].instanceType = INSTANCE_TYPE_DATANODE;
            dirHealth->dir[index].diskAlarm = (Alarm*) CmMalloc(sizeof(Alarm) * DISK_ALARM_CEIL);
            rc = strcpy_s(dirHealth->dir[index].path, MAX_DEVICE_DIR, dnDataPath);
            securec_check_errno(rc, (void)rc);
            index++;
        }
    }
    if (IsDiskHasDir(g_logBasePath, diskName)) {
        dirHealth->dir[index].instanceType = INSTANCE_TYPE_LOG;
        dirHealth->dir[index].diskAlarm = (Alarm*) CmMalloc(sizeof(Alarm) * DISK_ALARM_CEIL);
        rc = strcpy_s(dirHealth->dir[index].path, MAX_DEVICE_DIR, g_logBasePath);
        securec_check_errno(rc, (void)rc);
        index++;
    }
    if (IsDiskHasDir(g_currentNode->cmDataPath, diskName)) {
        dirHealth->dir[index].instanceType = INSTANCE_TYPE_CM;
        dirHealth->dir[index].diskAlarm = (Alarm*) CmMalloc(sizeof(Alarm) * DISK_ALARM_CEIL);
        rc = strcpy_s(dirHealth->dir[index].path, MAX_DEVICE_DIR, g_currentNode->cmDataPath);
        securec_check_errno(rc, (void)rc);
        index++;
    }
    InitDirAlarmItem(dirHealth);
    return CM_SUCCESS;
}

status_t InitDiskHealthCtx()
{
    uint32 diskCount = 0;
    char** diskNames = GetAllDiskNames(&diskCount);
    if (diskNames == NULL || diskCount == 0) {
        write_runlog(ERROR, "[%s] Failed to get disk info.\n", __FUNCTION__);
        return CM_ERROR;
    }
    g_diskHealth.diskCount = diskCount;
    g_diskHealth.disk = (DiskHealthItem*)malloc(sizeof(DiskHealthItem) * diskCount);
    if (g_diskHealth.disk == NULL) {
        write_runlog(ERROR, "[%s] Out of memory, get disk info failed.\n", __FUNCTION__);
        FreePtr2Ptr(diskNames, diskCount);
        return CM_ERROR;
    }
    errno_t rc = memset_s(g_diskHealth.disk, sizeof(DiskHealthItem) * diskCount, 0, sizeof(DiskHealthItem) * diskCount);
    securec_check_errno(rc, (void)rc);
    for (uint32 i = 0; i < diskCount; i++) {
        rc = strcpy_s(g_diskHealth.disk[i].diskName, MAX_DEVICE_DIR, diskNames[i]);
        securec_check_errno(rc, (void)rc);
        if (InitDirHealthCtx(g_diskHealth.disk[i].diskName, &g_diskHealth.disk[i].dirHealth)!= CM_SUCCESS) {
            write_runlog(ERROR, "Failed to init dir health check context.\n");
            FreePtr2Ptr(diskNames, diskCount);
            return CM_ERROR;
        }
        for (uint32 j = 0; j < g_diskHealth.disk[i].dirHealth.dirCount; ++j) {
            g_diskHealth.disk[i].dirHealth.dir[j].latestIoTime = GetMonotonicTimeMs();
        }
    }
    FreePtr2Ptr(diskNames, diskCount);
    return CM_SUCCESS;
}

char* DirStatusToString(DirStatus status)
{
    switch (status) {
        case DIR_STAT_INIT:
            return "init";
        case DIR_STAT_UNKNOWN:
            return "unknown";
        case DIR_STAT_NORMAL:
            return "normal";
        case DIR_STAT_NOT_EXIST:
            return "not exist";
        case DIR_STAT_NOT_DIR:
            return "not dir";
        case DIR_STAT_PERMISSION_DENIED:
            return "permission denied";
        default:
            break;
    }
    return "error";
}

char* DiskStatusToString(DiskStatus status)
{
    switch (status) {
        case DISK_STAT_INIT:
            return "init";
        case DISK_STAT_UNKNOWN:
            return "unknown";
        case DISK_STAT_NORMAL:
            return "normal";
        case DISK_STAT_HUNG:
            return "hung";
        default:
            break;
    }
    return "error";
}

void SetDirStatus(DirStatus* currentStatus, DirStatus newStatus, const char* dirPath)
{
    if ((*currentStatus) != newStatus) {
        write_runlog(LOG, "The status of dir(%s) changed from %s to %s.\n", dirPath, DirStatusToString(*currentStatus),
                     DirStatusToString(newStatus));
        *currentStatus = newStatus;
    }
}

void SetDiskStatus(DiskStatus* currentStatus, DiskStatus newStatus, const char* dirPath)
{
    if ((*currentStatus)!= newStatus) {
        write_runlog(LOG, "The status of disk(%s) changed from %s to %s.\n", dirPath,
            DiskStatusToString(*currentStatus), DiskStatusToString(newStatus));
        *currentStatus = newStatus;
    }
}

void CheckDirHealth(DirHealthItem* dirHealthItem)
{
    struct stat statBuf = {0};
    if (stat(dirHealthItem->path, &statBuf) != 0) {
        SetDirStatus(&dirHealthItem->dirStatus, DIR_STAT_NOT_EXIST, dirHealthItem->path);
        return;
    }
    if (!S_ISDIR(statBuf.st_mode)) {
        SetDirStatus(&dirHealthItem->dirStatus, DIR_STAT_NOT_DIR, dirHealthItem->path);
        return;
    }
    if ((statBuf.st_mode & S_IRWXU) != S_IRWXU) {
        SetDirStatus(&dirHealthItem->dirStatus, DIR_STAT_PERMISSION_DENIED, dirHealthItem->path);
        return;
    }
    SetDirStatus(&dirHealthItem->dirStatus, DIR_STAT_NORMAL, dirHealthItem->path);
}

DiskStatus GetDiskStatusByErrno(int err)
{
    switch (err) {
        case 0:
            return DISK_STAT_NORMAL;
        case EROFS:
            return DISK_STAT_READONLY;
        case EIO:
            return DISK_STAT_IO_ERROR;
        case ENOSPC:
            return DISK_STAT_NO_SPACE;
        default:
            break;
    }
    return DISK_STAT_BROKEN;
}

void CheckDiskRWSlow(DirHealthItem* dirHealthItem)
{
    uint64 costTime = (GetMonotonicTimeMs() - dirHealthItem->latestIoTime);
    AlarmType alarmType = ALM_AT_Resume;
    uint64 threshold = (g_diskCheckTimeout == 1) ? 1 : g_diskCheckTimeout * 4 / 5;
    if (threshold > 0 && costTime > threshold) {
        alarmType = ALM_AT_Fault;
    }
    ReportDiskSlowAlarm(&(dirHealthItem->diskAlarm[DISK_ALARM_READ_WRITE_SLOW]),
                        alarmType, costTime, threshold, dirHealthItem->path);
}

FILE* CheckOpenTestFile(const char* testFile, const char* openType, DirHealthItem* dirHealthItem, int* saveErrno)
{
    errno = 0;
    dirHealthItem->latestIoTime = GetMonotonicTimeMs();
    FILE* fp = fopen(testFile, openType);
    if (fp == NULL) {
        int tempErrno = errno;
        if (tempErrno == EMFILE) {
            write_runlog(ERROR, "To many files open, cma will exit.\n");
            _exit(1);
        }
        write_runlog(LOG, "Failed to open file(%s) with errno(%d).\n", testFile, tempErrno);
        if (tempErrno == EACCES || tempErrno == EROFS || tempErrno == EIO || tempErrno == ENOSPC
            || tempErrno == ENOENT) {
            (*saveErrno) = tempErrno;
            return NULL;
        }
    }
    (*saveErrno) = 0;
    return fp;
}

int CheckWriteTestFile(FILE* fp, uint32* writeSize, DirHealthItem* dirHealthItem)
{
    errno = 0;
    (void)pthread_rwlock_rdlock(&g_diskCheckBufferLock);
    dirHealthItem->latestIoTime = GetMonotonicTimeMs();
    size_t len = fwrite(g_diskCheckBuffer, g_diskCheckBufferSize, 1, fp);
    CheckDiskRWSlow(dirHealthItem);
    dirHealthItem->latestIoTime = GetMonotonicTimeMs();
    int saveErrno = errno;
    (*writeSize) = g_diskCheckBufferSize;
    (void)pthread_rwlock_unlock(&g_diskCheckBufferLock);
    if (len != 1) {
        write_runlog(ERROR, "Failed to write test file with errno(%d).\n", saveErrno);
        if (saveErrno == EROFS || saveErrno == EIO) {
            return saveErrno;
        } else {
            return 0;
        }
    }
    if (fsync(fileno(fp)) != 0) {
        write_runlog(ERROR, "Failed to fsync test file with errno(%d).\n", errno);
        return errno;
    }
    return 0;
}

int CheckReadTestFile(FILE* fp, uint32 readSize, DirHealthItem* dirHealthItem)
{
    char* readBuf = (char*)CmMalloc(sizeof(char) * readSize);
    errno = 0;
    dirHealthItem->latestIoTime = GetMonotonicTimeMs();
    size_t len = fread(readBuf, readSize, 1, fp);
    CheckDiskRWSlow(dirHealthItem);
    dirHealthItem->latestIoTime = GetMonotonicTimeMs();
    int saveErrno = errno;
    FREE_AND_RESET(readBuf);
    if (len != 1) {
        write_runlog(LOG, "Failed to read test file with errno(%d).\n", saveErrno);
        if (saveErrno == ENOSPC || saveErrno == EACCES) {
            return saveErrno;
        }
    }
    return 0;
}

int CheckWriteAndReadTestFile(const char* testFile, DirHealthItem* dirHealthItem)
{
    uint32 writeSize = 0;
    int err = 0;
    FILE* fp = CheckOpenTestFile(testFile, "we", dirHealthItem, &err);
    if (fp == NULL) {
        return err;
    }
    err = CheckWriteTestFile(fp, &writeSize, dirHealthItem);
    (void)fclose(fp);
    if (err != 0) {
        return err;
    }
    FILE* readFp = CheckOpenTestFile(testFile, "re", dirHealthItem, &err);
    if (readFp == NULL) {
        return err;
    }
    err = CheckReadTestFile(readFp, writeSize, dirHealthItem);
    (void)fclose(readFp);
    (void)remove(testFile);
    return err;
}

void CheckDiskHealth(DirHealthItem* dirHealthItem)
{
    if (dirHealthItem->dirStatus!= DIR_STAT_NORMAL) {
        write_runlog(DEBUG5, "Dir(%s) status(%d) is not normal, skip disk health check.\n",
            dirHealthItem->path, dirHealthItem->dirStatus);
        if (dirHealthItem->diskStatus == DISK_STAT_NORMAL) {
            SetDiskStatus(&dirHealthItem->diskStatus, DISK_STAT_UNKNOWN, dirHealthItem->path);
        }
        return;
    }
    char testFile[MAX_PATH_LEN] = {0};
    int ret = snprintf_s(testFile, MAX_PATH_LEN, MAX_PATH_LEN - 1, "%s/%s", dirHealthItem->path, DISK_TEST_FILENAME);
    securec_check_intval(ret, (void)ret);
    if (testFile[0] != '\0') {
        int err = CheckWriteAndReadTestFile(testFile, dirHealthItem);
        SetDiskStatus(&dirHealthItem->diskStatus, GetDiskStatusByErrno(err), dirHealthItem->path);
    } else {
        write_runlog(ERROR, "The test file of (%s) is empty, skip disk health check.\n", dirHealthItem->path);
    }
}

void* DiskItemHealthCheckMain(void* arg)
{
    DiskHealthItem* diskHealthItem = (DiskHealthItem*)arg;
    write_runlog(LOG, "Disk health check for disk(%s) started.\n", diskHealthItem->diskName);
    while (true) {
        if (g_shutdownRequest || g_exitFlag) {
            break;
        }
        if (g_diskCheckInterval == 0 || g_diskCheckBufferSize == 0 || g_diskCheckBuffer == NULL) {
            write_runlog(DEBUG5, "Disk health check config is invalid, g_diskCheckInterval=%u, "
                                 "g_diskCheckBufferSize=%u.\n", g_diskCheckInterval, g_diskCheckBufferSize);
            cm_sleep(1);
            continue;
        }
        for (uint32 i = 0; i < diskHealthItem->dirHealth.dirCount; i++) {
            CheckDirHealth(&diskHealthItem->dirHealth.dir[i]);
            CheckDiskHealth(&diskHealthItem->dirHealth.dir[i]);
        }
        cm_sleep(g_diskCheckInterval);
    }
    write_runlog(LOG, "Disk health check thread for disk(%s) exit.\n", diskHealthItem->diskName);
    return NULL;
}

status_t CreateDiskItemHealthCheckThread(DiskHealthItem* diskHealthItem)
{
    pthread_t threadId;
    if (pthread_create(&threadId, NULL, DiskItemHealthCheckMain, diskHealthItem) != 0) {
        write_runlog(ERROR, "Failed to create disk health check thread for disk(%s).\n", diskHealthItem->diskName);
        return CM_ERROR;
    }
    return CM_SUCCESS;
}

void CheckOneDiskHung(uint32 i, uint32 j)
{
    uint64 curTime = GetMonotonicTimeMs();
    uint64 costTime = (curTime - g_diskHealth.disk[i].dirHealth.dir[j].latestIoTime);
    AlarmType alarmType = ALM_AT_Resume;
    if (costTime > g_diskCheckTimeout) {
        g_diskHealth.disk[i].dirHealth.dir[j].diskStatus = DISK_STAT_HUNG;
        alarmType = ALM_AT_Fault;
    }
    ReportDiskHangAlarm(&(g_diskHealth.disk[i].dirHealth.dir[j].diskAlarm[DISK_ALARM_HUNG]), alarmType,
        g_diskHealth.disk[i].diskName, costTime, g_diskCheckTimeout);
}

void* DiskHealthCheckMonitorMain(void* arg)
{
    write_runlog(LOG, "Disk health check monitor thread started.\n");
    while (true) {
        if (g_shutdownRequest || g_exitFlag) {
            break;
        }
        for (uint32 i = 0; i < g_diskHealth.diskCount; i++) {
            for (uint32 j = 0; j < g_diskHealth.disk[i].dirHealth.dirCount; j++) {
                CheckOneDiskHung(i, j);
            }
        }
        cm_sleep(1);
    }
    write_runlog(LOG, "Disk health check monitor thread exit.\n");
    return NULL;
}

status_t CreateDiskHealthCheckMonitorThread()
{
    pthread_t threadId;
    if (pthread_create(&threadId, NULL, DiskHealthCheckMonitorMain, NULL)!= 0) {
        write_runlog(ERROR, "Failed to create disk health check monitor thread.\n");
        return CM_ERROR;
    }
    return CM_SUCCESS;
}

void CreateDiskHealthCheckThread()
{
#ifdef ENABLE_XALARMD
    if (g_enableXalarmdFeature) {
        struct alarm_subscription_info id_filter;
        id_filter.id_list[0] = (int)XALARM_SLOW_DISK_ID;
        id_filter.len = 1;
        g_xalarmClientId = xalarm_Register(HandleXalarm, id_filter);
        if (g_xalarmClientId < 0) {
            write_runlog(ERROR, "Failed to xalarm register, please check the status of xalarmd\n");
        } else {
            write_runlog(LOG, "xalarm register success, client id is %d\n", g_xalarmClientId);
        }
        return;
    } else {
        write_runlog(LOG, "Xalarm feature is disabled by configuration, use traditional disk check.\n");
    }
#else
    write_runlog(LOG, "Xalarm feature is disabled in compile time, use traditional disk check.\n");
#endif
    if (InitDiskHealthCtx() != CM_SUCCESS) {
        write_runlog(ERROR, "Failed to init disk health check context.\n");
        return;
    }
    uint32 totalDir = 0;
    for (uint32 i = 0; i < g_diskHealth.diskCount; i++) {
        write_runlog(LOG, "Start disk health check thread for disk(%s).\n", g_diskHealth.disk[i].diskName);
        if (CreateDiskItemHealthCheckThread(&g_diskHealth.disk[i])!= CM_SUCCESS) {
            write_runlog(FATAL, "Failed to create disk health check thread for disk(%s).\n",
                         g_diskHealth.disk[i].diskName);
            exit(1);
        }
        totalDir += g_diskHealth.disk[i].dirHealth.dirCount;
    }
    if (totalDir > 0) {
        write_runlog(LOG, "Start disk health check monitor thread for %u dirs.\n", totalDir);
        if (CreateDiskHealthCheckMonitorThread() != CM_SUCCESS) {
            write_runlog(FATAL, "Failed to create disk health check monitor thread.\n");
            exit(1);
        }
    }
}

uint32 GetDiskCheckTimeout()
{
    return g_diskCheckTimeout;
}

uint32 GetDiskCheckInterval()
{
    return g_diskCheckInterval;
}

uint32 GetDiskCheckBufferSize()
{
    return g_diskCheckBufferSize;
}