acrn-hypervisor/tools/acrn-manager/acrnctl.c
Yan, Like 26e7b260b1 Move ACRN tools code directory one level higher
The tools directory is moved out of ./devicemodle, to be in parallel with
hypervisor, devicemodel and doc.

Signed-off-by: Yan, Like <like.yan@intel.com>
2018-05-14 17:57:47 +08:00

730 lines
15 KiB
C

/*
* ProjectAcrn
* Acrnctl
*
* Copyright (C) 2018 Intel Corporation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*
* Author: Tao Yuhong <yuhong.tao@intel.com>
*/
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <sys/queue.h>
#include <sys/socket.h>
#include <sys/un.h>
#include "monitor_msg.h"
#define ACRNCTL_OPT_ROOT "/opt/acrn/conf"
/* helper functions */
static int shell_cmd(const char *cmd, char *outbuf, int len)
{
FILE *ptr;
char cmd_buf[256];
int ret;
if (!outbuf)
return system(cmd);
memset(cmd_buf, 0, sizeof(cmd_buf));
memset(outbuf, 0, len);
snprintf(cmd_buf, sizeof(cmd_buf), "%s 2>&1", cmd);
ptr = popen(cmd_buf, "re");
if (!ptr)
return -1;
ret = fread(outbuf, 1, len, ptr);
pclose(ptr);
return ret;
}
static void process_msg(struct vmm_msg *msg)
{
if (msg->len < sizeof(*msg))
return;
switch(msg->msgid) {
case MSG_STR:
printf("%s\n", msg->payload);
break;
default:
printf("Unknown msgid(%d) received\n", msg->msgid);
}
}
/* vm states data and helper functions */
#define ACRN_DM_SOCK_ROOT "/run/acrn"
struct vmm_struct {
char name[128];
unsigned long state;
LIST_ENTRY(vmm_struct) list;
};
enum vm_state {
VM_STATE_UNKNOWN = 0,
VM_CREATED, /* VM created / awaiting start (boot) */
VM_STARTED, /* VM started (booted) */
VM_PAUSED, /* VM paused */
VM_UNTRACKED, /* VM not created by acrnctl, or its launch script can change vm name */
};
static const char *state_str[] = {
[VM_STATE_UNKNOWN] = "unknown",
[VM_CREATED] = "stopped",
[VM_STARTED] = "started",
[VM_PAUSED] = "paused",
[VM_UNTRACKED] = "untracked",
};
static LIST_HEAD(vmm_list_struct, vmm_struct) vmm_head;
static struct vmm_struct *vmm_list_add(char *name)
{
struct vmm_struct *s;
s = calloc(1, sizeof(struct vmm_struct));
if (!s) {
perror("alloc vmm_struct");
return NULL;
}
strcpy(s->name, name);
LIST_INSERT_HEAD(&vmm_head, s, list);
return s;
}
static struct vmm_struct *vmm_find(char *name)
{
struct vmm_struct *s;
LIST_FOREACH(s, &vmm_head, list)
if (!strcmp(name, s->name))
return s;
return NULL;
}
static void vmm_update(void)
{
char cmd[128] = { };
char cmd_out[256] = { };
char *vmname;
char *pvmname = NULL;
struct vmm_struct *s;
snprintf(cmd, sizeof(cmd),
"find %s/add/ -name \"*.sh\" | "
"sed \"s/\\/opt\\/acrn\\/conf\\/add\\///g\" | "
"sed \"s/.sh//g\"", ACRNCTL_OPT_ROOT);
shell_cmd(cmd, cmd_out, sizeof(cmd_out));
vmname = strtok_r(cmd_out, "\n", &pvmname);
while (vmname) {
s = vmm_list_add(vmname);
if (!s)
continue;
s->state = VM_CREATED;
vmname = strtok_r(NULL, "\n", &pvmname);
}
pvmname = NULL;
snprintf(cmd, sizeof(cmd),
"find %s/ -name \"*.socket\" | "
"sed \"s/\\/run\\/acrn\\///g\" | "
"sed \"s/-monitor.socket//g\"", ACRN_DM_SOCK_ROOT);
shell_cmd(cmd, cmd_out, sizeof(cmd_out));
vmname = strtok_r(cmd_out, "\n", &pvmname);
while (vmname) {
s = vmm_find(vmname);
if (s)
s->state = VM_STARTED;
else {
s = vmm_list_add(vmname);
if (s)
s->state = VM_UNTRACKED;
}
vmname = strtok_r(NULL, "\n", &pvmname);
}
}
/* There are acrnctl cmds */
/* command: list */
static void acrnctl_list_help(void)
{
printf("acrnctl list\n"
"\tlist all VMs, shown in %s or %s\n",
ACRNCTL_OPT_ROOT, ACRN_DM_SOCK_ROOT);
}
static int acrnctl_do_list(int argc, char *argv[])
{
struct vmm_struct *s;
int find = 0;
if (argc == 2)
if (!strcmp("help", argv[1])) {
acrnctl_list_help();
return 0;
}
vmm_update();
LIST_FOREACH(s, &vmm_head, list) {
printf("%s\t\t%s\n", s->name, state_str[s->state]);
find++;
}
if (!find)
printf("There are no VMs\n");
return 0;
}
/* command: add */
static void acrnctl_add_help(void)
{
printf("acrnctl add [uos_bash_script]\n"
"\trequires an uos script file\n");
}
static int check_name(const char *name)
{
int i = 0, j = 0;
char illegal[] = "!@#$%^&*, ";
/* Name should start with a letter */
if ((name[0] < 'a' || name[0] > 'z')
&& (name[0] < 'A' || name[0] > 'Z')) {
printf("name not started with latter!\n");
return -1;
}
/* no illegal charactoer */
while (name[i]) {
j = 0;
while (illegal[j]) {
if (name[i] == illegal[j]) {
printf("vmname[%d] is '%c'!\n", i, name[i]);
return -1;
}
j++;
}
i++;
}
if (!strcmp(name, "help"))
return -1;
if (!strcmp(name, "nothing"))
return -1;
return 0;
}
static const char *acrnctl_bin_path;
static int find_acrn_dm;
static int write_tmp_file(int fd, int n, char *word[])
{
int len, ret, i = 0;
char buf[128];
if (!n)
return 0;
len = strlen(word[0]);
if (len >= strlen("acrn-dm")) {
if (!strcmp(word[0] + len - strlen("acrn-dm"), "acrn-dm")) {
find_acrn_dm++;
memset(buf, 0, sizeof(buf));
snprintf(buf, sizeof(buf), "%s gentmpfile",
acrnctl_bin_path);
ret = write(fd, buf, strlen(buf));
if (ret < 0)
return -1;
i++;
}
}
while (i < n) {
memset(buf, 0, sizeof(buf));
snprintf(buf, sizeof(buf), " %s", word[i]);
i++;
ret = write(fd, buf, strlen(buf));
if (ret < 0)
return -1;
}
ret = write(fd, "\n", 1);
if (ret < 0)
return -1;
return 0;
}
#define MAX_FILE_SIZE (4096 * 4)
#define MAX_WORD 64
#define FILE_NAME_LENGTH 128
#define TMP_FILE_SUFFIX ".acrnctl"
static int acrnctl_do_add(int argc, char *argv[])
{
struct vmm_struct *s;
int fd, fd_tmp, ret = 0;
char *buf;
char *word[MAX_WORD], *line;
char *word_p = NULL, *line_p = NULL;
int n_word;
char fname[FILE_NAME_LENGTH + sizeof(TMP_FILE_SUFFIX)];
char cmd[128];
char args[128];
int p, i;
char cmd_out[256];
char vmname[128];
if (argc < 2) {
acrnctl_add_help();
return -1;
}
if (!strcmp("help", argv[1])) {
acrnctl_add_help();
return 0;
}
if (strlen(argv[1]) >= FILE_NAME_LENGTH) {
printf("file name too long: %s\n", argv[1]);
return -1;
}
memset(args, 0, sizeof(args));
p = 0;
for (i = 2; i < argc; i++) {
if (p >= sizeof(args) - 1) {
args[sizeof(args) - 1] = 0;
printf("Too many optional args: %s\n", args);
return -1;
}
p += snprintf(&args[p], sizeof(args) - p, " %s", argv[i]);
}
args[p] = ' ';
fd = open(argv[1], O_RDONLY);
if (fd < 0) {
perror(argv[1]);
ret = -1;
goto open_read_file;
}
buf = calloc(1, MAX_FILE_SIZE);
if (!buf) {
perror("calloc for add vm");
ret = -1;
goto calloc_err;
}
ret = read(fd, buf, MAX_FILE_SIZE);
if (ret >= MAX_FILE_SIZE) {
printf("%s exceed MAX_FILE_SIZE:%d", argv[1], MAX_FILE_SIZE);
ret = -1;
goto file_exceed;
}
/* open tmp file for write */
memset(fname, 0, sizeof(fname));
snprintf(fname, sizeof(fname), "%s%s", argv[1], TMP_FILE_SUFFIX);
fd_tmp = open(fname, O_RDWR | O_CREAT | O_TRUNC, 0666);
if (fd_tmp < 0) {
perror(fname);
ret = -1;
goto open_tmp_file;
}
find_acrn_dm = 0;
line = strtok_r(buf, "\n", &line_p);
while (line) {
word_p = NULL;
n_word = 0;
word[n_word] = strtok_r(line, " ", &word_p);
while (word[n_word]) {
n_word++;
word[n_word] = strtok_r(NULL, " ", &word_p);
}
if (write_tmp_file(fd_tmp, n_word, word)) {
ret = -1;
perror(fname);
goto write_tmpfile;
}
line = strtok_r(NULL, "\n", &line_p);
}
if (!find_acrn_dm) {
printf
("Don't see 'acrn-dm' in %s, maybe it is in another script, "
"this is no supported for now\n", argv[1]);
ret = -1;
goto no_acrn_dm;
}
snprintf(cmd, sizeof(cmd), "mv %s %s.back", argv[1], argv[1]);
system(cmd);
snprintf(cmd, sizeof(cmd), "mv %s %s", fname, argv[1]);
system(cmd);
memset(vmname, 0, sizeof(vmname));
snprintf(cmd, sizeof(cmd), "bash %s%s >./%s.result", argv[1],
args, argv[1]);
ret = shell_cmd(cmd, cmd_out, sizeof(cmd_out));
if (ret < 0)
goto get_vmname;
snprintf(cmd, sizeof(cmd), "grep -a \"acrnctl: \" ./%s.result", argv[1]);
ret = shell_cmd(cmd, cmd_out, sizeof(cmd_out));
if (ret < 0)
goto get_vmname;
ret = sscanf(cmd_out, "acrnctl: %s", vmname);
if (ret != 1) {
ret = -1;
snprintf(cmd, sizeof(cmd), "cat ./%s.result", argv[1]);
shell_cmd(cmd, cmd_out, sizeof(cmd_out));
printf("%s can't reach acrn-dm, "
"please try again when you make sure it can launch an UOS\n"
"result:\n%s\n", argv[1], cmd_out);
goto get_vmname;
}
ret = check_name(vmname);
if (ret) {
printf("\"%s\" is a bad name, please select another name\n",
vmname);
goto get_vmname;
}
snprintf(cmd, sizeof(cmd), "mkdir -p %s/add", ACRNCTL_OPT_ROOT);
system(cmd);
vmm_update();
s = vmm_find(vmname);
if (s) {
printf("%s(%s) already exist, can't add %s%s\n",
vmname, state_str[s->state], argv[1], args);
ret = -1;
goto vm_exist;
}
snprintf(cmd, sizeof(cmd), "cp %s.back %s/add/%s.sh", argv[1],
ACRNCTL_OPT_ROOT, vmname);
system(cmd);
snprintf(cmd, sizeof(cmd), "echo %s >%s/add/%s.args", args,
ACRNCTL_OPT_ROOT, vmname);
system(cmd);
printf("%s added\n", vmname);
vm_exist:
get_vmname:
snprintf(cmd, sizeof(cmd), "rm -f ./%s.result", argv[1]);
system(cmd);
snprintf(cmd, sizeof(cmd), "mv %s %s", argv[1], fname);
system(cmd);
snprintf(cmd, sizeof(cmd), "mv %s.back %s", argv[1], argv[1]);
system(cmd);
no_acrn_dm:
snprintf(cmd, sizeof(cmd), "rm -f %s", fname);
system(cmd);
write_tmpfile:
close(fd_tmp);
open_tmp_file:
file_exceed:
free(buf);
calloc_err:
close(fd);
open_read_file:
return ret;
}
/* command: stop */
static void acrnctl_stop_help(void)
{
printf("acrnctl stop [vmname0] [vmname1] ...\n"
"\t run \"acrnctl list\" to get running VMs\n");
}
static int send_stop_msg(char *vmname)
{
int fd, ret;
struct sockaddr_un addr;
struct vmm_msg msg;
struct timeval timeout;
fd_set rfd, wfd;
char buf[128];
fd = socket(AF_UNIX, SOCK_STREAM, 0);
if (fd < 0) {
printf("%s %d\n", __FUNCTION__, __LINE__);
ret = -1;
goto sock_err;
}
addr.sun_family = AF_UNIX;
snprintf(addr.sun_path, sizeof(addr.sun_path), "%s/%s-monitor.socket",
ACRN_DM_SOCK_ROOT, vmname);
ret = connect(fd, (struct sockaddr *)&addr, sizeof(addr));
if (ret < 0) {
printf("%s %d\n", __FUNCTION__, __LINE__);
goto connect_err;
}
msg.magic = VMM_MSG_MAGIC;
msg.msgid = REQ_STOP;
msg.len = sizeof(msg);
timeout.tv_sec = 1; /* wait 1 second for read/write socket */
timeout.tv_usec = 0;
FD_ZERO(&rfd);
FD_ZERO(&wfd);
FD_SET(fd, &rfd);
FD_SET(fd, &wfd);
select(fd + 1, NULL, &wfd, NULL, &timeout);
if (!FD_ISSET(fd, &wfd)) {
printf("%s %d\n", __FUNCTION__, __LINE__);
goto cant_write;
}
ret = write(fd, &msg, sizeof(msg));
/* wait response */
select(fd + 1, &rfd, NULL, NULL, &timeout);
if (FD_ISSET(fd, &rfd)) {
memset(buf, 0, sizeof(buf));
ret = read(fd, buf, sizeof(buf));
if (ret <= sizeof(buf))
process_msg((void*)&buf);
}
cant_write:
connect_err:
close(fd);
sock_err:
return ret;
}
static int acrnctl_do_stop(int argc, char *argv[])
{
struct vmm_struct *s;
int i;
if (argc < 2) {
acrnctl_stop_help();
return -1;
}
if (!strcmp("help", argv[1])) {
acrnctl_stop_help();
return 0;
}
vmm_update();
for (i = 1; i < argc; i++) {
s = vmm_find(argv[i]);
if (!s) {
printf("can't find %s\n", argv[i]);
continue;
}
if (s->state == VM_CREATED) {
printf("%s is already (%s)\n", argv[i],
state_str[s->state]);
continue;
}
send_stop_msg(argv[i]);
}
return 0;
}
/* command: delete */
static void acrnctl_del_help(void)
{
printf("acrnctl del [vmname0] [vmname1] ...\n"
"\t run \"acrnctl list\" get VM names\n");
}
static int acrnctl_do_del(int argc, char *argv[])
{
struct vmm_struct *s;
int i;
char cmd[128];
if (argc < 2) {
acrnctl_del_help();
return -1;
}
if (!strcmp("help", argv[1])) {
acrnctl_del_help();
return 0;
}
vmm_update();
for (i = 1; i < argc; i++) {
s = vmm_find(argv[i]);
if (!s) {
printf("can't find %s\n", argv[i]);
continue;
}
if (s->state != VM_CREATED) {
printf("can't delete %s(%s)\n", argv[i],
state_str[s->state]);
continue;
}
snprintf(cmd, sizeof(cmd), "rm -f %s/add/%s.sh",
ACRNCTL_OPT_ROOT, argv[i]);
system(cmd);
snprintf(cmd, sizeof(cmd), "rm -f %s/add/%s.args",
ACRNCTL_OPT_ROOT, argv[i]);
system(cmd);
}
return 0;
}
/* command: start */
static void acrnctl_start_help(void)
{
printf("acrnctl start [vmname]\n"
"\t run \"acrnctl list\" get VM names\n"
"\t each time user can only start one VM\n");
}
static int acrnctl_do_start(int argc, char *argv[])
{
struct vmm_struct *s;
char cmd[128];
if (argc != 2) {
acrnctl_start_help();
return -1;
}
if (!strcmp("help", argv[1])) {
acrnctl_start_help();
return 0;
}
vmm_update();
s = vmm_find(argv[1]);
if (!s) {
printf("can't find %s\n", argv[1]);
return -1;
}
if (s->state != VM_CREATED) {
printf("can't start %s(%s)\n", argv[1], state_str[s->state]);
return -1;
}
snprintf(cmd, sizeof(cmd), "bash %s/add/%s.sh $(cat %s/add/%s.args)",
ACRNCTL_OPT_ROOT, argv[1], ACRNCTL_OPT_ROOT, argv[1]);
system(cmd);
return 0;
}
#define ACMD(CMD,FUNC) \
{.cmd = CMD, .func = FUNC,}
struct acrnctl_cmd {
const char *cmd;
int (*func) (int argc, char *argv[]);
} acmds[] = {
ACMD("list", acrnctl_do_list),
ACMD("start", acrnctl_do_start),
ACMD("stop", acrnctl_do_stop),
ACMD("del", acrnctl_do_del),
ACMD("add", acrnctl_do_add),
};
#define NCMD (sizeof(acmds)/sizeof(struct acrnctl_cmd))
static void help_info(void)
{
int i;
printf("support:\n");
for (i = 0; i < NCMD; i++)
printf("\t%s\n", acmds[i].cmd);
printf("Use acrnctl [cmd] help for details\n");
}
int main(int argc, char *argv[])
{
int i;
if (argc == 1) {
help_info();
return 0;
}
acrnctl_bin_path = argv[0];
/* first check acrnctl reserved operations */
if (!strcmp(argv[1], "gentmpfile")) {
printf("\nacrnctl: %s\n", argv[argc - 1]);
return 0;
}
for (i = 0; i < NCMD; i++)
if (!strcmp(argv[1], acmds[i].cmd))
return acmds[i].func(argc - 1, &argv[1]);
/* Reach here means unsupported command */
printf("Unknown command: %s\n", argv[1]);
help_info();
return -1;
}