/******************************************************************************
 * Copyright (c) Huawei Technologies Co., Ltd. 2020-2021. All rights reserved.
 * etmem/memRouter licensed under the 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.
 * Author: zhangxuzhou
 * Create: 2020-09-08
 * Description: init policy
 ******************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <numaif.h>
#include <numa.h>
#include <time.h>
#include <pthread.h>
#include <limits.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <json-c/json.h>
#include <json-c/json_util.h>
#include <json-c/json_object.h>

#include "memdcd_log.h"
#include "memdcd_policy_threshold.h"
#include "memdcd_policy.h"

#define FULL_PERMISSION 0777

static struct {
    char *name;
    enum mem_policy_type type;
    struct memdcd_policy_opt * (*get_opt)(void);
} mem_policy_opts[] = {
    {"mig_policy_threshold", POL_TYPE_THRESHOLD, get_threshold_policy},
    {NULL, POL_TYPE_MAX, NULL},
};

struct mem_policy g_policy;

static int check_policy_file_permission(const char *path)
{
    char error_str[ERROR_STR_MAX_LEN] = {0};
    struct stat permission_buffer;

    if (stat(path, &permission_buffer) != 0) {
        memdcd_log(_LOG_ERROR, "Get file stat failed. err: %s",
            strerror_r(errno, error_str, ERROR_STR_MAX_LEN));
        return -1;
    }

    if (permission_buffer.st_uid != geteuid()) {
        memdcd_log(_LOG_ERROR, "Owner of config file is not same with user of this process.");
        return -EACCES;
    }

    if ((permission_buffer.st_mode & FULL_PERMISSION) != (S_IRUSR | S_IWUSR)) {
        memdcd_log(_LOG_ERROR, "Access permission of config file is not 600.");
        return -EACCES;
    }

    return 0;
}

static int parse_policy_type(const char *path)
{
    int i;
    const char *str = NULL;

    json_object *root = json_object_from_file(path);
    if (root == NULL)
        return -1;

    json_object *type = json_object_object_get(root, "type");
    if (type == NULL)
        return -1;

    str = json_object_get_string(type);
    if (str == NULL)
        return -1;

    for (i = 0; mem_policy_opts[i].name != NULL; i++) {
        if (strcmp(str, mem_policy_opts[i].name) == 0) {
            return i;
        }
    }

    return -1;
}

int init_mem_policy(char *path)
{
    int ret;
    int type;
    struct memdcd_policy_opt *opt = NULL;
    char file_path[PATH_MAX] = {0};

    if (!realpath(path, file_path)) {
        memdcd_log(_LOG_ERROR, "Config file not exist.");
        return -EEXIST;
    }
    ret = check_policy_file_permission(file_path);
    if (ret != 0) {
        return ret;
    }

    type = parse_policy_type(file_path);
    if (type < 0)
        return -EINVAL;

    memdcd_log(_LOG_DEBUG, "%s: type: %d.", __func__, g_policy.type);

    opt = mem_policy_opts[type].get_opt();

    ret = opt->init(&g_policy, file_path);
    if (ret != 0)
        return ret;

    return 0;
}

struct mem_policy *get_policy(void)
{
    return &g_policy;
}