* Copyright (c) Huawei Technologies Co., Ltd. 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: liruilin
* Create: 2021-02-26
* Description: init memdcd daemon.
******************************************************************************/
#include <stdio.h>
#include <unistd.h>
#include <sys/un.h>
#include <sys/socket.h>
#include <signal.h>
#include <stdlib.h>
#include <stddef.h>
#include <errno.h>
#include "memdcd_log.h"
#include "memdcd_process.h"
#include "memdcd_cmd.h"
#include "memdcd_daemon.h"
#define MAX_PENDING_QUEUE_LENGTH 64
#define MAX_SOCK_PATH_LENGTH 108
static volatile sig_atomic_t g_sock_fd;
static volatile sig_atomic_t g_exit_signal;
static void _set_exit_flag(int s)
{
(void)s;
g_exit_signal = 1;
if (g_sock_fd > 0) {
close(g_sock_fd);
}
g_sock_fd = -1;
}
static void memdcd_install_signal(void)
{
signal(SIGINT, _set_exit_flag);
signal(SIGTERM, _set_exit_flag);
}
static int _set_socket_option(int sock_fd)
{
int rc;
int buf_len = MAX_MESSAGE_LENGTH;
struct timeval timeout = {5, 0};
char error_str[ERROR_STR_MAX_LEN] = {0};
rc = setsockopt(sock_fd, SOL_SOCKET, SO_SNDTIMEO, (const char *)&timeout, sizeof(timeout));
if (rc < 0) {
memdcd_log(_LOG_ERROR, "Set send timeout for socket failed. err: %s",
strerror_r(errno, error_str, ERROR_STR_MAX_LEN));
return -1;
}
rc = setsockopt(sock_fd, SOL_SOCKET, SO_RCVBUF, (const char *)&buf_len, sizeof(buf_len));
if (rc < 0) {
memdcd_log(_LOG_ERROR, "Set recive buffer length for socket failed. err: %s",
strerror_r(errno, error_str, ERROR_STR_MAX_LEN));
return -1;
}
rc = setsockopt(sock_fd, SOL_SOCKET, SO_SNDBUF, (const char *)&buf_len, sizeof(buf_len));
if (rc < 0) {
memdcd_log(_LOG_ERROR, "Set send buffer length for socket failed. err: %s",
strerror_r(errno, error_str, ERROR_STR_MAX_LEN));
return -1;
}
return 0;
}
static int memdcd_server_init(const char *sock_path)
{
int sock_fd;
struct sockaddr_un sock_addr;
size_t sock_len;
char error_str[ERROR_STR_MAX_LEN] = {0};
memset(&sock_addr, 0, sizeof(struct sockaddr_un));
sock_fd = socket(AF_UNIX, SOCK_STREAM, 0);
if (sock_fd < 0) {
memdcd_log(_LOG_ERROR, "Create socket for fail. err: %s", strerror_r(errno, error_str, ERROR_STR_MAX_LEN));
return -1;
}
sock_len = strlen(sock_path);
if (sock_len >= MAX_SOCK_PATH_LENGTH) {
memdcd_log(_LOG_ERROR, "Socket path is too long.");
close(sock_fd);
return -1;
}
sock_addr.sun_family = AF_UNIX;
memcpy(sock_addr.sun_path, sock_path, sock_len);
sock_addr.sun_path[0] = 0;
sock_len += offsetof(struct sockaddr_un, sun_path);
if (_set_socket_option(sock_fd) != 0) {
memdcd_log(_LOG_ERROR, "Set for socket fail. err: %s", strerror_r(errno, error_str, ERROR_STR_MAX_LEN));
close(sock_fd);
return -1;
}
if (bind(sock_fd, (struct sockaddr *)&sock_addr, sock_len) != 0) {
memdcd_log(_LOG_ERROR, "Socket bind %s fail. err: %s",
(char *)&sock_addr.sun_path[1], strerror_r(errno, error_str, ERROR_STR_MAX_LEN));
close(sock_fd);
return -1;
}
return sock_fd;
}
static int check_socket_permission(int sock_fd)
{
struct ucred cred;
socklen_t len;
ssize_t rc;
char error_str[ERROR_STR_MAX_LEN] = {0};
len = sizeof(struct ucred);
rc = getsockopt(sock_fd,
SOL_SOCKET,
SO_PEERCRED,
&cred,
&len);
if (rc < 0) {
memdcd_log(_LOG_ERROR, "Getsockopt failed. err: %s\n", strerror_r(errno, error_str, ERROR_STR_MAX_LEN));
return -1;
}
if (cred.uid != 0 || cred.gid != 0) {
memdcd_log(_LOG_ERROR, "Socket connect failed, need recieving from app of root privilege.\n");
return -1;
}
return 0;
}
static int memdcd_accept(char *recv_buf)
{
int accp_fd = -1;
int ret = 0;
ssize_t rc;
char error_str[ERROR_STR_MAX_LEN] = {0};
accp_fd = accept(g_sock_fd, NULL, NULL);
if (accp_fd < 0) {
memdcd_log(_LOG_ERROR, "Accept message failed. err: %s", strerror_r(errno, error_str, ERROR_STR_MAX_LEN));
return -1;
}
rc = check_socket_permission(accp_fd);
if (rc != 0) {
ret = rc;
goto close_fd;
}
rc = recv(accp_fd, recv_buf, MAX_MESSAGE_LENGTH, 0);
if (rc <= 0) {
memdcd_log(_LOG_WARN, "Socket recive from client fail. err: %s", \
strerror_r(errno, error_str, ERROR_STR_MAX_LEN));
ret = -1;
goto close_fd;
}
if (rc > MAX_MESSAGE_LENGTH) {
memdcd_log(_LOG_WARN, "Buffer sent to memdcd is too long, should be less than %d.", MAX_MESSAGE_LENGTH);
ret = -1;
goto close_fd;
}
memdcd_log(_LOG_DEBUG, "Memdcd got one connection.");
ret = rc;
close_fd:
close(accp_fd);
return ret;
}
void *memdcd_daemon_start(const char *sock_path)
{
char *recv_buf = NULL;
int msg_len;
int sock_fd;
char error_str[ERROR_STR_MAX_LEN] = {0};
if (sock_path == NULL) {
return NULL;
}
g_exit_signal = 0;
g_sock_fd = 0;
memdcd_install_signal();
sock_fd = memdcd_server_init(sock_path);
if (sock_fd < 0)
return NULL;
recv_buf = (char *)malloc(sizeof(char) * MAX_MESSAGE_LENGTH);
if (recv_buf == NULL) {
memdcd_log(_LOG_ERROR, "Failed to alloc buffer to receive message.");
close(sock_fd);
sock_fd = -1;
return NULL;
}
if (listen(sock_fd, MAX_PENDING_QUEUE_LENGTH) != 0) {
memdcd_log(_LOG_ERROR, "Error listening on socket %s. err: %s",
sock_path, strerror_r(errno, error_str, ERROR_STR_MAX_LEN));
close(sock_fd);
sock_fd = -1;
goto free_buf;
}
if (g_sock_fd < 0) {
close(sock_fd);
sock_fd = -1;
goto free_buf;
}
g_sock_fd = sock_fd;
memdcd_log(_LOG_INFO, "Start listening on %s.", sock_path);
while (g_exit_signal == 0) {
msg_len = memdcd_accept(recv_buf);
if (msg_len < 0) {
memdcd_log(_LOG_ERROR, "Error accepting message. err: %s", strerror_r(errno, error_str, ERROR_STR_MAX_LEN));
continue;
}
if (handle_recv_buffer(recv_buf, msg_len) < 0)
memdcd_log(_LOG_DEBUG, "Error handling message.");
}
migrate_process_exit();
if (g_sock_fd > 0) {
close(g_sock_fd);
g_sock_fd = -1;
}
free_buf:
free(recv_buf);
return NULL;
}