* Copyright (c) 2022 Huawei Technologies Co.,Ltd.
*
* GR 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.
* -------------------------------------------------------------------------
*
* gr_protocol.c
*
*
* IDENTIFICATION
* src/common/gr_protocol.c
*
* -------------------------------------------------------------------------
*/
#include "gr_errno.h"
#include "gr_log.h"
#include "gr_thv.h"
#include "gr_protocol.h"
static char *g_gr_cmd_desc[GR_CMD_TYPE_OFFSET(GR_CMD_END)] = {
[GR_CMD_TYPE_OFFSET(GR_CMD_MKDIR)] = "mkdir",
[GR_CMD_TYPE_OFFSET(GR_CMD_RMDIR)] = "rmdir",
[GR_CMD_TYPE_OFFSET(GR_CMD_MOUNT_VFS)] = "mount vfs",
[GR_CMD_TYPE_OFFSET(GR_CMD_UNMOUNT_VFS)] = "unmount vfs",
[GR_CMD_TYPE_OFFSET(GR_CMD_QUERY_FILE_NUM)] = "query file num",
[GR_CMD_TYPE_OFFSET(GR_CMD_QUERY_FILE_INFO)] = "query file info",
[GR_CMD_TYPE_OFFSET(GR_CMD_OPEN_FILE)] = "open file",
[GR_CMD_TYPE_OFFSET(GR_CMD_CLOSE_FILE)] = "close file",
[GR_CMD_TYPE_OFFSET(GR_CMD_CREATE_FILE)] = "create file",
[GR_CMD_TYPE_OFFSET(GR_CMD_DELETE_FILE)] = "delete file",
[GR_CMD_TYPE_OFFSET(GR_CMD_WRITE_FILE)] = "write file",
[GR_CMD_TYPE_OFFSET(GR_CMD_APPEND_FILE)] = "append file",
[GR_CMD_TYPE_OFFSET(GR_CMD_READ_FILE)] = "read file",
[GR_CMD_TYPE_OFFSET(GR_CMD_RENAME_FILE)] = "rename file",
[GR_CMD_TYPE_OFFSET(GR_CMD_STAT_FILE)] = "stat file",
[GR_CMD_TYPE_OFFSET(GR_CMD_LOAD_CTRL)] = "load ctrl",
[GR_CMD_TYPE_OFFSET(GR_CMD_UPDATE_GRITTEN_SIZE)] = "update written size",
[GR_CMD_TYPE_OFFSET(GR_CMD_STOP_SERVER)] = "stopserver",
[GR_CMD_TYPE_OFFSET(GR_CMD_SETCFG)] = "setcfg",
[GR_CMD_TYPE_OFFSET(GR_CMD_SET_MAIN_INST)] = "set main inst",
[GR_CMD_TYPE_OFFSET(GR_CMD_SWITCH_LOCK)] = "switch cm lock",
[GR_CMD_TYPE_OFFSET(GR_CMD_POSTPONE_FILE_TIME)] = "extend file expired time",
[GR_CMD_TYPE_OFFSET(GR_CMD_HANDSHAKE)] = "handshake with server",
[GR_CMD_TYPE_OFFSET(GR_CMD_RELOAD_CERTS)] = "reload gr server certs",
[GR_CMD_TYPE_OFFSET(GR_CMD_GET_DISK_USAGE)] = "get disk usage",
[GR_CMD_TYPE_OFFSET(GR_CMD_EXIST)] = "exist item",
[GR_CMD_TYPE_OFFSET(GR_CMD_GET_FTID_BY_PATH)] = "get ftid by path",
[GR_CMD_TYPE_OFFSET(GR_CMD_GETCFG)] = "getcfg",
[GR_CMD_TYPE_OFFSET(GR_CMD_GET_INST_STATUS)] = "get inst status",
[GR_CMD_TYPE_OFFSET(GR_CMD_GET_TIME_STAT)] = "get time stat",
[GR_CMD_TYPE_OFFSET(GR_CMD_EXEC_REMOTE)] = "exec remote",
[GR_CMD_TYPE_OFFSET(GR_CMD_QUERY_HOTPATCH)] = "query status of hotpatch",
};
char *gr_get_cmd_desc(gr_cmd_type_e cmd_type)
{
if (cmd_type < GR_CMD_BEGIN || cmd_type >= GR_CMD_END) {
return "unknown";
}
return g_gr_cmd_desc[GR_CMD_TYPE_OFFSET(cmd_type)];
}
typedef status_t (*recv_func_t)(void *link, char *buf, uint32_t size, int32_t *recv_size, uint32 *wait_event);
typedef status_t (*recv_timed_func_t)(void *link, char *buf, uint32_t size, uint32_t timeout);
typedef status_t (*send_timed_func_t)(void *link, const char *buf, uint32_t size, uint32_t timeout);
typedef status_t (*wait_func_t)(void *link, uint32_t wait_for, int32_t timeout, bool32 *ready);
typedef struct st_vio {
recv_func_t vio_recv;
wait_func_t vio_wait;
recv_timed_func_t vio_recv_timed;
send_timed_func_t vio_send_timed;
} vio_t;
static const vio_t g_vio_list[] = {
{NULL, NULL, NULL, NULL},
{(recv_func_t)cs_tcp_recv, (wait_func_t)cs_tcp_wait, (recv_timed_func_t)cs_tcp_recv_timed,
(send_timed_func_t)cs_tcp_send_timed},
{NULL, NULL, NULL, NULL},
{(recv_func_t)cs_uds_recv, (wait_func_t)cs_uds_wait, (recv_timed_func_t)cs_uds_recv_timed,
(send_timed_func_t)cs_uds_send_timed},
{(recv_func_t)cs_ssl_recv, (wait_func_t)cs_ssl_wait, (recv_timed_func_t)cs_ssl_recv_timed,
(send_timed_func_t)cs_ssl_send_timed},
{NULL, NULL, NULL, NULL},
{NULL, NULL, NULL, NULL},
};
Macro definitions for pipe I/O operations
@note
Performance sensitive, the pipe->type should be guaranteed by the caller.
e.g. CS_TYPE_TCP, CS_TYPE_SSL, CS_TYPE_DOMAIN_SOCKET
*/
#define GET_VIO(pipe) (&g_vio_list[(pipe)->type])
#define VIO_SEND_TIMED(pipe, buf, size, timeout) GET_VIO(pipe)->vio_send_timed(&(pipe)->link, buf, size, timeout)
#define VIO_RECV(pipe, buf, size, len, wait_event) GET_VIO(pipe)->vio_recv(&(pipe)->link, buf, size, len, wait_event)
#define VIO_RECV_TIMED(pipe, buf, size, timeout) GET_VIO(pipe)->vio_recv_timed(&(pipe)->link, buf, size, timeout)
#define VIO_WAIT(pipe, ev, timeout, ready) GET_VIO(pipe)->vio_wait(&(pipe)->link, ev, timeout, ready)
status_t gr_put_text(gr_packet_t *pack, text_t *text)
{
errno_t errcode;
CM_ASSERT(pack != NULL);
CM_ASSERT(text != NULL);
(void)gr_put_int32(pack, text->len);
if (text->len == 0) {
return CM_SUCCESS;
}
errcode = memcpy_s(GR_WRITE_ADDR(pack), GR_REMAIN_SIZE(pack), text->str, text->len);
GR_SECUREC_RETURN_IF_ERROR(errcode, CM_ERROR);
pack->head->size += CM_ALIGN4(text->len);
return CM_SUCCESS;
}
status_t gr_put_str_with_cutoff(gr_packet_t *pack, const char *str)
{
uint32_t size;
errno_t errcode = 0;
CM_ASSERT(pack != NULL);
CM_ASSERT(str != NULL);
size = (uint32_t)strlen(str);
char *addr = GR_WRITE_ADDR(pack);
if (size != 0) {
if (GR_REMAIN_SIZE(pack) <= 1) {
size = 0;
} else if (size >= GR_REMAIN_SIZE(pack)) {
size = GR_REMAIN_SIZE(pack) - 1;
}
errcode = memcpy_s(addr, GR_REMAIN_SIZE(pack), str, size);
GR_SECUREC_RETURN_IF_ERROR(errcode, CM_ERROR);
}
GR_WRITE_ADDR(pack)[size] = '\0';
pack->head->size += CM_ALIGN4(size + 1);
return CM_SUCCESS;
}
status_t gr_write_packet(cs_pipe_t *pipe, gr_packet_t *pack)
{
if (pack->head->size > GR_MAX_PACKET_SIZE) {
GR_RETURN_IFERR2(CM_ERROR, CM_THROW_ERROR(ERR_BUFFER_OVERFLOW, pack->head->size, GR_MAX_PACKET_SIZE));
}
status_t status = VIO_SEND_TIMED(pipe, pack->buf, pack->head->size, GR_DEFAULT_NULL_VALUE);
GR_RETURN_IFERR2(status, CM_THROW_ERROR(ERR_PACKET_SEND, pack->buf_size, pack->head->size, pack->head->size));
return CM_SUCCESS;
}
status_t gr_write(cs_pipe_t *pipe, gr_packet_t *pack)
{
CM_ASSERT(pipe != NULL);
CM_ASSERT(pack != NULL);
pack->options = pipe->options;
return gr_write_packet(pipe, pack);
}
static status_t gr_read_packet(cs_pipe_t *pipe, gr_packet_t *pack, bool32 cs_client)
{
int32_t remain_size, offset, recv_size;
bool32 ready = CM_FALSE;
uint32 wait_event;
offset = 0;
status_t status;
char *cs_mes = cs_client ? "read wait for server response" : "read wait for client request";
for (;;) {
status = VIO_RECV(pipe, pack->buf + offset, (uint32_t)(pack->buf_size - offset), &recv_size, &wait_event);
GR_RETURN_IFERR2(status, GR_THROW_ERROR(ERR_TCP_RECV, "uds", cm_get_sock_error()));
offset += recv_size;
if (offset >= (int32_t)sizeof(gr_packet_head_t)) {
break;
}
status = VIO_WAIT(pipe, CS_WAIT_FOR_READ, CM_NETWORK_IO_TIMEOUT, &ready);
GR_RETURN_IFERR2(status, GR_THROW_ERROR(ERR_TCP_TIMEOUT, cs_mes));
if (!ready) {
GR_RETURN_IFERR2(CM_ERROR, GR_THROW_ERROR(ERR_GR_TCP_TIMEOUT_REMAIN, (uint32_t)(sizeof(uint32_t) - offset)));
}
}
if (pack->head->size > pack->buf_size) {
GR_THROW_ERROR_EX(ERR_TCP_RECV, "Receive protocol failed, head size is %u, buffer size is %u, errno %d.",
pack->head->size, pack->buf_size, cm_get_sock_error());
cm_fync_logfile();
CM_ASSERT(0);
}
remain_size = (int32_t)pack->head->size - offset;
if (remain_size <= 0) {
return CM_SUCCESS;
}
status = VIO_WAIT(pipe, CS_WAIT_FOR_READ, CM_NETWORK_IO_TIMEOUT, &ready);
GR_RETURN_IFERR2(status, GR_THROW_ERROR(ERR_TCP_TIMEOUT, cs_mes));
if (!ready) {
GR_THROW_ERROR(ERR_TCP_TIMEOUT, cs_mes);
return CM_ERROR;
}
status = VIO_RECV_TIMED(pipe, pack->buf + offset, (uint32_t)remain_size, CM_NETWORK_IO_TIMEOUT);
GR_RETURN_IFERR2(status, GR_THROW_ERROR(ERR_TCP_RECV, "Receive protocol failed."));
return CM_SUCCESS;
}
status_t gr_read(cs_pipe_t *pipe, gr_packet_t *pack, bool32 cs_client)
{
CM_ASSERT(pipe != NULL);
CM_ASSERT(pack != NULL);
pack->options = pipe->options;
return gr_read_packet(pipe, pack, cs_client);
}
static status_t gr_call_base(cs_pipe_t *pipe, gr_packet_t *req, gr_packet_t *ack)
{
bool32 ready = CM_FALSE;
if (gr_write(pipe, req) != CM_SUCCESS) {
LOG_RUN_ERR("gr write failed.");
return CM_ERROR;
}
if (cs_wait(pipe, CS_WAIT_FOR_READ, pipe->socket_timeout, &ready) != CM_SUCCESS) {
LOG_RUN_ERR("cs wait failed.");
return CM_ERROR;
}
if (!ready) {
GR_RETURN_IFERR2(
CM_ERROR, GR_THROW_ERROR(ERR_SOCKET_TIMEOUT, pipe->socket_timeout / (int32_t)CM_TIME_THOUSAND_UN));
}
return gr_read(pipe, ack, CM_TRUE);
}
status_t gr_call_ex(cs_pipe_t *pipe, gr_packet_t *req, gr_packet_t *ack)
{
status_t ret = gr_call_base(pipe, req, ack);
if (ret != CM_SUCCESS) {
LOG_RUN_ERR("[GR] ABORT INFO: gr call server failed, ack command type:%d, application exit.", ack->head->cmd);
GR_THROW_ERROR(ERR_GR_CALL_SERVER_FAILED);
cs_disconnect(pipe);
cm_fync_logfile();
}
return ret;
}