acrn-hypervisor/misc/services/life_mngr/command_handler.c

487 lines
14 KiB
C

/*
* Copyright (C)2021-2022 Intel Corporation.
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/un.h>
#include <libgen.h>
#include "uart.h"
#include "uart_channel.h"
#include "command.h"
#include "socket.h"
#include "command_handler.h"
#include "log.h"
#include "config.h"
bool system_shutdown_flag;
bool get_system_shutdown_flag(void)
{
return system_shutdown_flag;
}
bool user_vm_reboot_flag;
bool system_reboot_flag;
bool system_reboot_request_flag;
bool get_vm_reboot_flag(void)
{
return user_vm_reboot_flag | system_reboot_flag;
}
/**
* @brief check whether all acrn-dm instance have been exit or not
*
* @return true all acrn-dm instance have been exit
* @return false at least one acrn-dm exist
*/
static bool wait_post_vms_shutdown(void)
{
FILE *fp = NULL;
char command[64], buf[8];
char *endptr, *ret_str;
long val;
int check_time = SHUTDOWN_TIMEOUT/5;
bool all_done = false;
snprintf(command, sizeof(command), "pgrep -u root -f acrn-dm | wc -l");
do {
fp = popen(command, "r");
ret_str = fgets(buf, sizeof(buf), fp);
if (ret_str == NULL)
LOG_WRITE("Failed to check acrn-dm process\n");
val = strtol(buf, &endptr, 10) - 1;
if (val == 0) {
all_done = true;
pclose(fp);
break;
}
check_time--;
LOG_PRINTF("Wait post launched VMs shutdown check_time:%d, Running VM num:%ld\n",
check_time, val);
pclose(fp);
sleep(5);
} while (check_time > 0);
return all_done;
}
static void start_system_reboot(void)
{
static bool platform_reboot;
if (is_uart_channel_connection_list_empty(channel) && (!platform_reboot)) {
platform_reboot = true;
LOG_WRITE("UART connection list is empty, will trigger system reboot\n");
close_socket(sock_server);
stop_listen_uart_channel_dev(channel);
if (wait_post_vms_shutdown()) {
LOG_WRITE("Service VM starts to reboot.\n");
system_reboot_flag = true;
} else {
LOG_WRITE("Some User VMs failed to power off, cancelled the platform reboot process.\n");
}
}
}
static void start_system_shutdown(void)
{
static bool platform_shutdown;
if (is_uart_channel_connection_list_empty(channel) && (!platform_shutdown)) {
platform_shutdown = true;
LOG_WRITE("UART connection list is empty, will trigger shutdown system\n");
close_socket(sock_server);
stop_listen_uart_channel_dev(channel);
if (wait_post_vms_shutdown()) {
LOG_WRITE("Service VM starts to power off.\n");
system_shutdown_flag = true;
} else {
LOG_WRITE("Some User VMs failed to power off, cancelled the platform shutdown process.\n");
}
}
}
static int send_socket_ack(void *arg, int fd, char *ack)
{
int ret = 0;
struct socket_dev *sock = (struct socket_dev *)arg;
struct socket_client *client = NULL;
client = find_socket_client(sock, fd);
if (client == NULL)
return -1;
LOG_PRINTF("Receive shutdown request from unix socket, fd=%d\n", client->fd);
memset(client->buf, 0, CLIENT_BUF_LEN);
memcpy(client->buf, ack, strlen(ack));
client->len = strlen(ack);
ret = write_socket_char(client);
LOG_PRINTF("Send acked message to unix socket, message=%s\n", ack);
return ret;
}
int socket_req_shutdown_service_vm_handler(void *arg, int fd)
{
int ret;
usleep(LISTEN_INTERVAL + SECOND_TO_US);
ret = send_socket_ack(arg, fd, ACK_REQ_SYS_SHUTDOWN);
if (ret < 0)
return 0;
start_all_uart_channel_dev_resend(channel, POWEROFF_CMD, VM_SHUTDOWN_RETRY_TIMES);
notify_all_connected_uart_channel_dev(channel, POWEROFF_CMD);
start_system_shutdown();
return 0;
}
static int req_user_vm_shutdown_reboot(void *arg, int fd, char *msg, char *ack_msg)
{
int ret;
struct channel_dev *c_dev = NULL;
struct socket_dev *sock = (struct socket_dev *)arg;
struct socket_client *client = NULL;
usleep(LISTEN_INTERVAL + SECOND_TO_US);
client = find_socket_client(sock, fd);
if (client == NULL)
return -1;
c_dev = find_uart_channel_dev_by_name(channel, client->name);
if (c_dev == NULL) {
(void) send_socket_ack(arg, fd, USER_VM_DISCONNECT);
LOG_PRINTF("Failed to fail to find uart device to communicate with user VM (%s)\n",
client->name);
return 0;
}
ret = send_socket_ack(arg, fd, ack_msg);
if (ret < 0) {
LOG_WRITE("Failed to send ACK by socket\n");
return 0;
}
LOG_PRINTF("Foward (%s) to user VM (%s) by UART\n", msg, c_dev->name);
start_uart_channel_dev_resend(c_dev, msg, MIN_RESEND_TIME);
ret = send_message_by_uart(c_dev->uart_device, msg, strlen(msg));
if (ret < 0)
LOG_PRINTF("Failed to foward (%s) to user VM by UART\n", msg);
return ret;
}
int socket_req_user_vm_shutdown_handler(void *arg, int fd)
{
return req_user_vm_shutdown_reboot(arg, fd, USER_VM_SHUTDOWN, ACK_REQ_USER_VM_SHUTDOWN);
}
int socket_req_user_vm_reboot_handler(void *arg, int fd)
{
return req_user_vm_shutdown_reboot(arg, fd, USER_VM_REBOOT, ACK_REQ_USER_VM_REBOOT);
}
int req_system_shutdown_reboot(void *arg, int fd, char *msg, char *ack_msg)
{
int ret;
struct channel_dev *c_dev = NULL;
usleep(LISTEN_INTERVAL + SECOND_TO_US);
c_dev = (struct channel_dev *)LIST_FIRST(&channel->tty_conn_head);
if (c_dev == NULL) {
(void) send_socket_ack(arg, fd, USER_VM_DISCONNECT);
LOG_WRITE("User VM is disconnect\n");
return 0;
}
ret = send_socket_ack(arg, fd, ack_msg);
if (ret < 0) {
LOG_WRITE("Failed to send ACK by socket\n");
return 0;
}
LOG_PRINTF("Foward (%s) req to service VM by UART\n", msg);
start_uart_channel_dev_resend(c_dev, msg, MIN_RESEND_TIME);
ret = send_message_by_uart(c_dev->uart_device, msg, strlen(msg));
if (ret < 0)
LOG_PRINTF("Failed to foward (%s) to service VM by UART\n", msg);
return ret;
}
int socket_req_system_reboot_user_vm_handler(void *arg, int fd)
{
return req_system_shutdown_reboot(arg, fd, REQ_SYS_REBOOT, ACK_REQ_SYS_REBOOT);
}
int socket_req_system_shutdown_user_vm_handler(void *arg, int fd)
{
return req_system_shutdown_reboot(arg, fd, REQ_SYS_SHUTDOWN, ACK_REQ_SYS_SHUTDOWN);
}
static int is_allowed_s5_channel_dev(struct life_mngr_config *conf, struct channel_dev *c_dev)
{
return strncmp(get_allow_s5_config(conf), get_uart_dev_path(c_dev->uart_device),
TTY_PATH_MAX);
}
static int is_allowed_sysreboot_channel_dev(struct life_mngr_config *conf, struct channel_dev *c_dev)
{
return strncmp(get_allow_sysreboot_config(conf), get_uart_dev_path(c_dev->uart_device),
TTY_PATH_MAX);
}
/**
* @brief The handler of sync command of lifecycle manager in service VM
*
* @param arg uart channel device instance
* @param fd the file directory of the uart which receives message
* @return indicate this command is handled successful or not
*/
int sync_cmd_handler(void *arg, int fd)
{
struct channel_dev *c_dev = NULL;
struct uart_channel *c = (struct uart_channel *)arg;
c_dev = find_uart_channel_dev(c, fd);
if (c_dev == NULL)
return 0;
(void)send_message_by_uart(c_dev->uart_device, ACK_SYNC, strlen(ACK_SYNC));
LOG_PRINTF("Receive sync message from user VM (%s), start to talk.\n",
c_dev->name);
usleep(2 * WAIT_RECV);
return 0;
}
int req_reboot_handler(void *arg, int fd)
{
int ret;
struct channel_dev *c_dev = NULL;
struct uart_channel *c = (struct uart_channel *)arg;
c_dev = find_uart_channel_dev(c, fd);
if (c_dev == NULL)
return 0;
if (is_allowed_sysreboot_channel_dev(&life_conf, c_dev)) {
LOG_PRINTF("The user VM (%s) is not allowed to trigger system reboot\n",
c_dev->name);
return 0;
}
LOG_PRINTF("Receive reboot request from user VM (%s)\n", c_dev->name);
ret = send_message_by_uart(c_dev->uart_device, ACK_REQ_SYS_REBOOT,
strlen(ACK_REQ_SYS_REBOOT));
if (ret < 0)
LOG_WRITE("Sending a reboot acknowledgement message to user VM failed.\n");
system_reboot_request_flag = true;
usleep(SECOND_TO_US);
LOG_PRINTF("Send acked shutdown request message to user VM (%s)\n", c_dev->name);
start_all_uart_channel_dev_resend(c, POWEROFF_CMD, VM_SHUTDOWN_RETRY_TIMES);
notify_all_connected_uart_channel_dev(c, POWEROFF_CMD);
usleep(2 * WAIT_RECV);
return ret;
}
/**
* @brief The handler of system shutdown request command of lifecycle manager in service VM
*
* @param arg uart channel device instance
* @param fd the file directory of the uart which receives message
* @return indicate this command is handled successful or not
*/
int req_shutdown_handler(void *arg, int fd)
{
int ret;
struct channel_dev *c_dev = NULL;
struct uart_channel *c = (struct uart_channel *)arg;
c_dev = find_uart_channel_dev(c, fd);
if (c_dev == NULL)
return 0;
if (is_allowed_s5_channel_dev(&life_conf, c_dev)) {
LOG_PRINTF("The user VM (%s) is not allowed to trigger system shutdown\n",
c_dev->name);
return 0;
}
LOG_PRINTF("Receive shutdown request from user VM (%s)\n", c_dev->name);
ret = send_message_by_uart(c_dev->uart_device, ACK_REQ_SYS_SHUTDOWN,
strlen(ACK_REQ_SYS_SHUTDOWN));
if (ret < 0)
LOG_WRITE("Sending a shutdown acknowledgement message to user VM failed.\n");
usleep(SECOND_TO_US);
LOG_PRINTF("Send acked shutdown request message to user VM (%s)\n", c_dev->name);
start_all_uart_channel_dev_resend(c, POWEROFF_CMD, VM_SHUTDOWN_RETRY_TIMES);
notify_all_connected_uart_channel_dev(c, POWEROFF_CMD);
usleep(2 * WAIT_RECV);
return ret;
}
/**
* @brief The handler of acked poweroff command of lifecycle manager in service VM
*
* @param arg uart channel instance
* @param fd the file directory of the uart which receives message
* @return indicate this command is handled successful or not
*/
int ack_poweroff_handler(void *arg, int fd)
{
struct channel_dev *c_dev = NULL;
struct uart_channel *c = (struct uart_channel *)arg;
c_dev = find_uart_channel_dev(c, fd);
if (c_dev == NULL)
return 0;
LOG_PRINTF("Receive poweroff ACK from user VM (%s)\n", c_dev->name);
stop_uart_channel_dev_resend(c_dev);
disconnect_uart_channel_dev(c_dev, c);
usleep(WAIT_USER_VM_POWEROFF);
if (system_reboot_request_flag) {
start_system_reboot();
} else {
start_system_shutdown();
}
return 0;
}
/**
* @brief The handler of ACK timeout command of lifecycle manager in service VM
*
* @param arg uart channel instance
* @param fd the file directory of the uart which receives message
* @return indicate this command is handled successful or not
*/
int ack_timeout_handler(void *arg, int fd)
{
struct channel_dev *c_dev = NULL;
struct uart_channel *c = (struct uart_channel *)arg;
c_dev = find_uart_channel_dev(c, fd);
if (c_dev == NULL)
return 0;
if (strncmp(c_dev->resend_buf, POWEROFF_CMD, strlen(POWEROFF_CMD)) == 0)
ack_poweroff_handler(arg, fd);
else
stop_uart_channel_dev_resend(c_dev);
return 0;
}
static int ack_user_vm_cmd(void *arg, int fd, char *ack_msg)
{
struct channel_dev *c_dev = NULL;
struct uart_channel *c = (struct uart_channel *)arg;
c_dev = find_uart_channel_dev(c, fd);
if (c_dev == NULL)
return 0;
LOG_PRINTF("Receive (%s) from user VM (%s)\n", ack_msg, c_dev->name);
stop_uart_channel_dev_resend(c_dev);
return 0;
}
int ack_user_vm_shutdown_cmd_handler(void *arg, int fd)
{
return ack_user_vm_cmd(arg, fd, ACK_USER_VM_SHUTDOWN);
}
int ack_user_vm_reboot_cmd_handler(void *arg, int fd)
{
return ack_user_vm_cmd(arg, fd, ACK_USER_VM_REBOOT);
}
/**
* @brief The handler of acked sync command of lifecycle manager in user VM
*
* @param arg uart channel device instance
* @param fd the file directory of the uart which receives message
* @return indicate this command is handled successful or not
*/
int acked_sync_handler(void *arg, int fd)
{
struct channel_dev *c_dev = NULL;
struct uart_channel *c = (struct uart_channel *)arg;
c_dev = find_uart_channel_dev(c, fd);
if (c_dev == NULL)
return 0;
LOG_WRITE("Receive acked sync message from service VM\n");
return 0;
}
/**
* @brief The handler of acked system shutdown request command of lifecycle manager in user VM
*
* @param arg uart channel instance
* @param fd the file directory of the uart which receives message
* @return indicate this command is handled successful or not
*/
int acked_req_shutdown_reboot_handler(void *arg, int fd)
{
struct channel_dev *c_dev = NULL;
struct uart_channel *c = (struct uart_channel *)arg;
c_dev = find_uart_channel_dev(c, fd);
if (c_dev == NULL)
return 0;
stop_uart_channel_dev_resend(c_dev);
LOG_WRITE("Receive shutdown request ACK from service VM\n");
return 0;
}
static int user_vm_shutdown_reboot(struct uart_channel *c, int fd, char *ack, bool reboot)
{
int ret;
struct channel_dev *c_dev = NULL;
c_dev = find_uart_channel_dev(c, fd);
if (c_dev == NULL)
return 0;
ret = send_message_by_uart(c_dev->uart_device, ack, strlen(ack));
if (ret < 0) {
LOG_PRINTF("Failed to send (%s) to service VM\n", ack);
}
disconnect_uart_channel_dev(c_dev, c);
usleep(2 * WAIT_RECV);
close_socket(sock_server);
if (reboot) {
user_vm_reboot_flag = true;
} else {
system_shutdown_flag = true;
}
return 0;
}
/**
* @brief The handler of poweroff command of lifecycle manager in user VM
*
* @param arg uart channel device instance
* @param fd the file directory of the uart which receives message
* @return indicate this command is handled successful or not
*/
int poweroff_cmd_handler(void *arg, int fd)
{
struct uart_channel *c = (struct uart_channel *)arg;
(void) user_vm_shutdown_reboot(c, fd, ACK_POWEROFF, false);
return 0;
}
int user_vm_shutdown_cmd_handler(void *arg, int fd)
{
struct uart_channel *c = (struct uart_channel *)arg;
(void) user_vm_shutdown_reboot(c, fd, ACK_USER_VM_SHUTDOWN, false);
return 0;
}
int user_vm_reboot_cmd_handler(void *arg, int fd)
{
struct uart_channel *c = (struct uart_channel *)arg;
(void) user_vm_shutdown_reboot(c, fd, ACK_USER_VM_REBOOT, true);
return 0;
}
/**
* @brief The handler of ACK timeout command of lifecycle manager in user VM
*
* @param arg uart channel instance
* @param fd the file directory of the uart which receives message
* @return indicate this command is handled successful or not
*/
int ack_timeout_default_handler(void *arg, int fd)
{
struct channel_dev *c_dev = NULL;
struct uart_channel *c = (struct uart_channel *)arg;
c_dev = find_uart_channel_dev(c, fd);
if (c_dev == NULL)
return 0;
stop_uart_channel_dev_resend(c_dev);
disconnect_uart_channel_dev(c_dev, c);
close_socket(sock_server);
LOG_PRINTF("Failed to receive ACK message from service VM (fd = %d)\n", fd);
return 0;
}