43cade60创建于 2022年2月24日历史提交
/*
 * Copyright (c) 2022 Huawei Technologies Co.,Ltd.
 *
 * openGauss 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.
 * -------------------------------------------------------------------------
 *
 * dcc_ctl_execute.c
 *    Client tool
 *
 * IDENTIFICATION
 *    src/tools/dcc_ctl/dcc_ctl_execute.c
 *
 * -------------------------------------------------------------------------
 */
#include <signal.h>
#include "dcc_ctl_execute.h"
#include "dcc_cmd_parse.h"
#include "clt_interface.h"
#include "cm_signal.h"

#ifdef __cplusplus
extern "C" {
#endif

#define CTL_PRODUCT_NAME        "DCC"
#define CTL_NAME                "dcc_ctl"
#define CTL_WATCH_DELETE        "DELETE"
#define CTL_WATCH_PUT           "PUT"
#define CTL_SLEEP_500_MS        500
#define CTL_EXE_OK              "OK\n"
#define CTL_CONNECT_ERR         "connect to server failed\n"
#define CTL_FETCH_ERR           "still has values...\n"
#define CTL_WATCH_ERR           "unkown watch type\n"
#define CTL_SEQUENCE_BUF_LEN    (11)

static void *g_ctl_handle = NULL;
static dcc_result_t g_dcc_result = {0};

static inline void ctl_signal_handler(int signum)
{
    if (signum == SIGTERM || signum == SIGQUIT || signum == SIGINT) {
        dcc_close(&g_ctl_handle);
        exit(EXIT_SUCCESS);
    }
}

static inline void ctl_print_text(uint32 len, const char *text)
{
    if (len == 0) {
        return;
    }
    ctl_printf("%.*s\n", len, text);
}

static void ctl_show_version(void)
{
    ctl_printf("%s\n", dcc_clt_get_version());
}

static void ctl_show_usage(void)
{
    ctl_printf(CTL_PRODUCT_NAME " Developer Command - Line(" CTL_NAME ") help\n");
    ctl_printf("\nUsage:\n");
    ctl_printf("dcc_ctl [options]\n");
    ctl_printf("dcc_ctl [options] command [command options] [command arguments...]\n");

    ctl_printf("\nOptions:\n");
    ctl_printf("    --help, -h       Shows help information\n");
    ctl_printf("    --version, -v,   Shows version information\n");
    ctl_printf("    --endpoints,     Specifies the dcc server list(Example: \"127.0.0.1:1888,127.0.0.1:2888\")\n");
    ctl_printf("    --user,          Specifies the username/password\n");
    ctl_printf("    --cacert,        Specifies the root certificate file used for SSL authentication\n");
    ctl_printf("    --cert,          Specifies the client certificate file used for SSL authentication\n");
    ctl_printf("    --key,           Specifies the key file used for SSL authentication\n");
    ctl_printf("    --timeout,       Command execution timeout threshold\n");

    ctl_printf("\nCommands:\n");

    ctl_printf("    --get       Queries the value of a specified key\n");
    ctl_printf("        Command options:\n");
    ctl_printf("            --prefix: prefix matching\n");
    ctl_printf("            --readlevel 1: read from leader, 2: read from connected node, 3: consistent read\n");

    ctl_printf("    --getchildren       Queries the prefixed keys\n");
    ctl_printf("        Command options:\n");
    ctl_printf("            --readlevel 1: read from leader, 2: read from connected node, 3: consistent read\n");

    ctl_printf("    --put       key val     Updates or insert the value of a specified key\n");
    ctl_printf("        Command options:\n");
    ctl_printf("            --sequence : create an incremental sequence\n");
    ctl_printf("            --expect : compare and swap\n");

    ctl_printf("    --delete    key         Deletes the specified key\n");
    ctl_printf("        Command options:\n");
    ctl_printf("           --prefix: Prefix matching delete\n");
    ctl_printf("    --watch     key\n");
    ctl_printf("        Command options:\n");
    ctl_printf("           --prefix: Prefix matching watch\n");
    ctl_printf("    --lease create leasename ttl: Create a lease with leasename and ttl\n");
    ctl_printf("    --lease renew leasename: Renew a specified lease to keep it alive\n");
    ctl_printf("    --lease query leasename: Query a specified lease info\n");
    ctl_printf("\n");
}

static void ctl_show_help(void)
{
    ctl_show_version();
    ctl_show_usage();
}

static inline void ctl_print_error(void)
{
    int error_no = dcc_get_errorno();
    if (error_no == DCC_CLI_BAD_ARGUMENTS) {
        ctl_show_help();
    } else {
        ctl_printf("%s\n", dcc_get_error(error_no));
    }
}

static inline void ctl_init_open_option(dcc_open_option_t *open_option, ctl_command_t *cmd)
{
    open_option->time_out = cmd->global_option.time_out;
    open_option->server_list = cmd->global_option.server_list;
    open_option->ca_file = cmd->global_option.ca_cert_file;
    open_option->crt_file = cmd->global_option.cert_file;
    open_option->key_file = cmd->global_option.key_file;
}

static status_t ctl_get_connect(void **handle, ctl_command_t *ctl_command)
{
    dcc_open_option_t open_option = {0};
    open_option.clt_name = CTL_NAME;
    ctl_init_open_option(&open_option, ctl_command);
    if (dcc_open(&open_option, handle) != CM_SUCCESS) {
        ctl_printf(CTL_CONNECT_ERR);
        return CM_ERROR;
    }
    return CM_SUCCESS;
}

static inline void ctl_init_get_option(dcc_option_t *option, const ctl_command_t *cmd)
{
    option->get_op.prefix = cmd->command_option.prefix;
    option->get_op.read_level = cmd->command_option.read_level;
}

static status_t clt_init_dcc_result(void)
{
    g_dcc_result.key = malloc(MAX_KEY_SIZE);
    if (g_dcc_result.key == NULL) {
        return CM_ERROR;
    }
    g_dcc_result.val = malloc(MAX_VAL_SIZE);
    if (g_dcc_result.val == NULL) {
        return CM_ERROR;
    }

    return CM_SUCCESS;
}

static status_t dcc_ctl_fetch(void)
{
    int ret;
    while (g_dcc_result.eof == CM_FALSE) {
        ret = dcc_fetch(g_ctl_handle, &g_dcc_result);
        if (ret != CM_SUCCESS) {
            ctl_printf(CTL_FETCH_ERR);
            return CM_ERROR;
        }
        ctl_print_text(g_dcc_result.key_len, g_dcc_result.key);
        ctl_print_text(g_dcc_result.val_len, g_dcc_result.val);
    }
    return CM_SUCCESS;
}

static status_t ctl_execute_get(ctl_command_t *cmd)
{
    CM_RETURN_IFERR(ctl_get_connect(&g_ctl_handle, cmd));
    CM_RETURN_IFERR(clt_init_dcc_result());
    dcc_option_t option = {0};
    ctl_init_get_option(&option, cmd);

    dcc_string_t key = {.data = cmd->key, .len = cmd->key_len};
    int ret = dcc_get(g_ctl_handle, &key, &option, &g_dcc_result);
    if (ret == CM_SUCCESS) {
        if (option.get_op.prefix == 0) {
            ctl_print_text(g_dcc_result.val_len, g_dcc_result.val);
            return CM_SUCCESS;
        } else {
            ctl_print_text(g_dcc_result.key_len, g_dcc_result.key);
            ctl_print_text(g_dcc_result.val_len, g_dcc_result.val);
            return dcc_ctl_fetch();
        }
    } else {
        ctl_print_error();
        return CM_ERROR;
    }
}

static status_t ctl_execute_getchildren(ctl_command_t *cmd)
{
    dcc_array_t result = {0};
    dcc_option_t option = {0};
    CM_RETURN_IFERR(ctl_get_connect(&g_ctl_handle, cmd));
    option.getchildren_op.read_level = cmd->command_option.read_level;
    dcc_string_t key = {.data = cmd->key, .len = cmd->key_len};
    int ret = dcc_getchildren(g_ctl_handle, &key, &option, &result);
    if (ret == CM_SUCCESS) {
        for (uint32 i = 0; i < result.count; i++) {
            ctl_print_text(result.strings[i]->len, result.strings[i]->data);
        }
        dcc_deinit_array(&result);
        return CM_SUCCESS;
    } else {
        ctl_print_error();
        return CM_ERROR;
    }
}


static inline void ctl_init_put_option(dcc_option_t *option, const ctl_command_t *cmd)
{
    option->put_op.expect_val_len = cmd->command_option.expect_val_len;
    option->put_op.expect_value = cmd->command_option.expect_val;
    option->put_op.sequence = cmd->command_option.sequence;
    option->put_op.lease_name.len = cmd->command_option.lease_opt.lease_name_len;
    option->put_op.lease_name.data = cmd->command_option.lease_opt.lease_name;
}

static status_t ctl_execute_put(ctl_command_t *cmd)
{
    CM_RETURN_IFERR(ctl_get_connect(&g_ctl_handle, cmd));
    dcc_option_t option = {0};
    char path[CTL_SEQUENCE_BUF_LEN];
    MEMS_RETURN_IFERR(memset_sp(path, CTL_SEQUENCE_BUF_LEN, 0, CTL_SEQUENCE_BUF_LEN));
    ctl_init_put_option(&option, cmd);
    dcc_string_t key = {.data = cmd->key, .len = cmd->key_len};
    dcc_string_t val = {.data = cmd->val, .len = cmd->val_len};
    dcc_string_t sequence = {.data = path, .len = CTL_SEQUENCE_BUF_LEN};
    int ret = dcc_put(g_ctl_handle, &key, &val, &option, &sequence);
    if (ret == CM_SUCCESS) {
        ctl_printf(CTL_EXE_OK);
        return CM_SUCCESS;
    } else {
        ctl_print_error();
        return CM_ERROR;
    }
}

static inline void ctl_init_delete_option(dcc_option_t *option, const ctl_command_t *cmd)
{
    option->delete_op.prefix = cmd->command_option.prefix;
}

static status_t ctl_execute_delete(ctl_command_t *cmd)
{
    CM_RETURN_IFERR(ctl_get_connect(&g_ctl_handle, cmd));
    dcc_option_t option = {0};
    ctl_init_delete_option(&option, cmd);
    dcc_string_t key = {.len = cmd->key_len, .data = cmd->key};
    int ret = dcc_delete(g_ctl_handle, &key, &option);
    if (ret == CM_SUCCESS) {
        ctl_printf(CTL_EXE_OK);
        return CM_SUCCESS;
    } else {
        ctl_print_error();
        return CM_ERROR;
    }
}

static int watch_proc(const char *key, unsigned int key_size, const dcc_watch_result_t *watch_result)
{
    switch (watch_result->watch_event) {
        case DCC_WATCH_EVENT_DELETE:
            ctl_print_text((uint32) strlen(CTL_WATCH_DELETE), CTL_WATCH_DELETE);
            ctl_print_text(key_size, key);
            break;
        case DCC_WATCH_EVENT_PUT:
            ctl_print_text((uint32) strlen(CTL_WATCH_PUT), CTL_WATCH_PUT);
            ctl_print_text(key_size, key);
            ctl_print_text(watch_result->data_changed_result.new_data_size, watch_result->data_changed_result.new_data);
            break;
        default:
            ctl_print_text((uint32) strlen(CTL_WATCH_ERR), CTL_WATCH_ERR);
            break;
    }
    return CM_SUCCESS;
}

static inline void ctl_register_signal(void)
{
    (void) cm_regist_signal(SIGTERM, ctl_signal_handler);
    (void) cm_regist_signal(SIGQUIT, ctl_signal_handler);
    (void) cm_regist_signal(SIGINT, ctl_signal_handler);
}

static status_t ctl_execute_watch(ctl_command_t *cmd)
{
    CM_RETURN_IFERR(ctl_get_connect(&g_ctl_handle, cmd));
    dcc_option_t option = {0};
    option.watch_op.prefix = cmd->command_option.prefix == CM_TRUE ? 1 : 0;
    dcc_string_t key = {.data = cmd->key, .len = cmd->key_len};
    int ret = dcc_watch(g_ctl_handle, &key, watch_proc, &option);
    ctl_register_signal();
    if (ret == CM_SUCCESS) {
        while (CM_TRUE) {
            cm_sleep(CTL_SLEEP_500_MS);
        }
        return CM_SUCCESS;
    } else {
        ctl_print_error();
        return CM_ERROR;
    }
}

static status_t ctl_execute_lease(ctl_command_t *cmd)
{
    CM_RETURN_IFERR(ctl_get_connect(&g_ctl_handle, cmd));
    int ret;
    dcc_string_t lease = { .data = cmd->command_option.lease_opt.lease_name,
        .len = cmd->command_option.lease_opt.lease_name_len };
    clt_lease_opt_type_e opt_type = cmd->command_option.lease_opt.opt_type;
    if (opt_type == CTL_LEASE_CREATE) {
        uint32 ttl = cmd->command_option.lease_opt.ttl;
        ret = dcc_lease_create(g_ctl_handle, &lease, ttl, CM_FALSE);
    } else if (opt_type == CTL_LEASE_RENEW) {
        ret = dcc_lease_keep_alive(g_ctl_handle, &lease);
    } else if (opt_type == CTL_LEASE_DESTROY) {
        ret = dcc_lease_destroy(g_ctl_handle, &lease);
    } else if (opt_type == CTL_LEASE_QUERY) {
        dcc_lease_info_t lease_info;
        ret = dcc_lease_query(g_ctl_handle, &lease, &lease_info);
        if (ret == CM_SUCCESS) {
            ctl_printf("lease %s with ttl(%us), remain_ttl(%us)\n", lease.data, lease_info.ttl, lease_info.remain_ttl);
            return CM_SUCCESS;
        }
    } else {
        ret = CM_ERROR;
    }

    if (ret == CM_SUCCESS) {
        ctl_printf(CTL_EXE_OK);
    } else {
        ctl_print_error();
        return CM_ERROR;
    }

    return CM_SUCCESS;
}

status_t ctl_execute_process(ctl_command_t *ctl_command)
{
    status_t ret = CM_SUCCESS;
    switch (ctl_command->type) {
        case CTL_KEYWORD_VERSION:
            ctl_show_version();
            break;
        case CTL_KEYWORD_HELP:
            ctl_show_help();
            break;
        case CTL_KEYWORD_GET:
            ret = ctl_execute_get(ctl_command);
            break;
        case CTL_KEYWORD_PUT:
            ret = ctl_execute_put(ctl_command);
            break;
        case CTL_KEYWORD_DELETE:
            ret = ctl_execute_delete(ctl_command);
            break;
        case CTL_KEYWORD_WATCH:
            ret = ctl_execute_watch(ctl_command);
            break;
        case CTL_KEYWORD_LEASE:
            ret = ctl_execute_lease(ctl_command);
            break;
        case CTL_KEYWORD_GETCHILDREN:
            ctl_execute_getchildren(ctl_command);
            break;
        default:
            ret = CM_ERROR;
    }
    dcc_close(&g_ctl_handle);
    CM_FREE_PTR(g_dcc_result.key);
    CM_FREE_PTR(g_dcc_result.val);
    return ret;
}

#ifdef __cplusplus
}
#endif