acrn-hypervisor/misc/services/life_mngr/command_handler.c
Xiangyang Wu ecf99c45a3 Misc: life_mngr: support user VM reboot
Add user VM reboot command and related command handler in
lifecycle manager to support user VM reboot.

Libvirt will send user VM reboot command to lifecycle manager
of service VM through socket, this command is forwarded to the
specified user VM, user VM will execute reboot command to start
reboot itself.

v1-->v2:
	Update some interfaces name to make it reable:
        (1) enable_uart_channel_dev_resend -->
	start_uart_channel_dev_resend
	(2) enable_all_uart_channel_dev_resend -->
	start_all_uart_channel_dev_resend
	(3) disable_uart_channel_dev_resend -->
	stop_uart_channel_dev_resend
	(4) get_reboot_flag --> get_user_vm_reboot_flag

Tracked-On: #5921

Signed-off-by: Xiangyang Wu <xiangyang.wu@intel.com>
Acked-by: Wang, Yu1 <yu1.wang@intel.com>
2022-03-03 14:40:04 +08:00

417 lines
12 KiB
C

/*
* Copyright (C)2021 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 get_user_vm_reboot_flag(void)
{
return user_vm_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_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 shut down 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 socket_req_system_shutdown_user_vm_handler(void *arg, int fd)
{
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_REQ_SYS_SHUTDOWN);
if (ret < 0) {
LOG_WRITE("Failed to send ACK by socket\n");
return 0;
}
LOG_WRITE("Foward shutdown req to service VM by UART\n");
start_uart_channel_dev_resend(c_dev, REQ_SYS_SHUTDOWN, MIN_RESEND_TIME);
ret = send_message_by_uart(c_dev->uart_device, REQ_SYS_SHUTDOWN, strlen(REQ_SYS_SHUTDOWN));
if (ret < 0)
LOG_WRITE("Failed to foward system shutdown request to service VM by UART\n");
return ret;
}
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);
}
/**
* @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;
}
/**
* @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("Send acked message to user VM fail\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);
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_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;
}