acrn-hypervisor/tools/acrn-manager/acrn_vm_ops.c
Jiang,Mao e50c0c88fb tools: acrn-manager: fix the possibility of creating directory at will by no permission process
There are several duplicate definitions for check_dir, it can check or create directory at will. However, only acrnd and dm monitor can create the directory. This commit fixs the possibility of creating directory at will by no permission process, which adds a param flags to conctrl if it should create the directory. By the way, this commit collates related MACRO into the same file , deletes the duplicate definitions in another files and fixs some format issues.

Tracked-On: #2886
Signed-off-by: Mao Jiang <maox.jiang@intel.com>
Acked-by: Yan, Like <like.yan@intel.com>
2019-04-15 17:01:03 +08:00

459 lines
9.2 KiB
C

/**
* Copyright (C) 2018 Intel Corporation
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <limits.h>
#include <pthread.h>
#include <dirent.h>
#include <sys/stat.h>
#include "acrnctl.h"
#include "acrn_mngr.h"
#include "mevent.h"
#include "vmm.h"
const char *state_str[] = {
[VM_STATE_UNKNOWN] = "unknown",
[VM_CREATED] = "stopped",
[VM_STARTED] = "started",
[VM_PAUSED] = "paused",
[VM_SUSPENDED] = "suspended",
[VM_UNTRACKED] = "untracked",
};
/* List head of all vm */
static pthread_mutex_t vmmngr_mutex = PTHREAD_MUTEX_INITIALIZER;
struct vmmngr_list_struct vmmngr_head = { NULL };
static unsigned long update_count = 0;
struct vmmngr_struct *vmmngr_find(const char *name)
{
struct vmmngr_struct *s;
LIST_FOREACH(s, &vmmngr_head, list)
if (!strcmp(name, s->name))
return s;
return NULL;
}
static int send_msg(const char *vmname, struct mngr_msg *req, struct mngr_msg *ack);
static int query_state(const char *name)
{
struct mngr_msg req;
struct mngr_msg ack;
int ret;
req.magic = MNGR_MSG_MAGIC;
req.msgid = DM_QUERY;
req.timestamp = time(NULL);
ret = send_msg(name, &req, &ack);
if (ret) {
printf("%s: Error to quary %s state, err: %d\n", __func__, name, ret);
return ret;
}
if (ack.data.state < 0) {
fprintf(stderr, "%s ack.data.state:%d\n", __FUNCTION__, ack.data.state);
}
return ack.data.state;
}
/*
* get vmname and pid from /run/acrn/mngr/[vmname].monitor.[pid].socket
*/
static inline int _get_vmname_pid(const char *src, char *p_vmname,
int max_len_vmname, int *pid)
{
char *p = NULL;
p = strchr(src, '.');
/* p - src: length of the substring "vmname" in the sting "src" */
if (!p || p - src == 0 || p - src >= max_len_vmname)
return -1;
else
strncpy(p_vmname, src, p - src);
/* move the pointer to the "pid" in the string "src" */
if (strncmp(".monitor.", p, strlen(".monitor.")))
return -1;
else
p = p + strlen(".monitor.");
*pid = strtol(p, NULL, 10);
if ((errno == ERANGE && (*pid == LONG_MAX || *pid == LONG_MIN))
|| (errno != 0 && *pid == 0))
return -1;
p = strchr(p, '.');
if (!p || strncmp(".socket", p, strlen(".socket")))
return -1;
return 0;
}
/* find all the running DM process, which has */
/* /run/acrn/mngr/[vmname].monitor.[pid].socket */
static void _scan_alive_vm(void)
{
DIR *dir;
struct dirent *entry;
struct vmmngr_struct *vm;
char name[PATH_LEN];
int pid;
int ret;
ret = check_dir(ACRN_DM_SOCK_PATH, CHK_ONLY);
if (ret) {
printf("%s: Failed to check directory %s, err: %d\n", __func__, ACRN_DM_SOCK_PATH, ret);
return;
}
dir = opendir(ACRN_DM_SOCK_PATH);
if (!dir) {
printf("%s: Failed to open directory %s\n", __func__, ACRN_DM_SOCK_PATH);
return;
}
while ((entry = readdir(dir))) {
memset(name, 0, sizeof(name));
ret = _get_vmname_pid(entry->d_name, name, sizeof(name), &pid);
if (ret < 0)
continue;
if (name[sizeof(name) - 1]) {
/* truncate name and go a head */
name[sizeof(name) - 1] = 0;
printf("%s: Truncate name as %s\n", __func__, name);
}
vm = vmmngr_find(name);
if (!vm) {
vm = calloc(1, sizeof(*vm));
if (!vm) {
printf("%s: Failed to alloc mem for %s\n", __func__, name);
continue;
}
memcpy(vm->name, name, sizeof(vm->name) - 1);
LIST_INSERT_HEAD(&vmmngr_head, vm, list);
}
ret = query_state(name);
if (ret < 0)
/* unsupport query */
vm->state_tmp = VM_STARTED;
else
switch (ret) {
case VM_SUSPEND_NONE:
vm->state_tmp = VM_STARTED;
break;
case VM_SUSPEND_SUSPEND:
vm->state_tmp = VM_SUSPENDED;
break;
default:
fprintf(stderr, "Warnning: unknow vm state:0x%lx\n",
vm->state);
vm->state_tmp = VM_STATE_UNKNOWN;
}
vm->update = update_count;
}
closedir(dir);
}
/*
* get vmname and suffix from src,
* which has [vmname].[suffix]
*/
static inline int _get_vmname_suffix(const char *src,
char *name, int max_len_name, char *suffix, int max_len_suffix)
{
char *p = NULL;
p = strchr(src, '.');
/* p - src: length of the substring vmname in the string src*/
if (!p || p - src == 0)
return -1;
strncpy(name, src, p - src);
if (p - src >= max_len_name) {
/* truncate name and go a head */
name[max_len_name - 1] = '\0';
printf("%s: Truncate name as %s\n", __func__, name);
}
strncpy(suffix, p + 1, max_len_suffix);
if (strncmp(suffix, "sh", strlen("sh")))
return -1;
return 0;
}
static void _scan_added_vm(void)
{
DIR *dir;
struct dirent *entry;
struct vmmngr_struct *vm;
char name[PATH_LEN];
char suffix[PATH_LEN];
int ret;
ret = check_dir(ACRN_CONF_PATH, CHK_ONLY);
if (ret) {
printf("%s: Failed to check directory %s, err: %d\n", __func__, ACRN_CONF_PATH, ret);
return;
}
ret = check_dir(ACRN_CONF_PATH_ADD, CHK_ONLY);
if (ret) {
printf("%s: Failed to check directory %s, err: %d\n", __func__, ACRN_CONF_PATH_ADD, ret);
return;
}
dir = opendir(ACRN_CONF_PATH_ADD);
if (!dir) {
printf("%s: Failed to open directory %s\n", __func__, ACRN_CONF_PATH_ADD);
return;
}
while ((entry = readdir(dir))) {
ret = strnlen(entry->d_name, sizeof(entry->d_name));
if (ret >= sizeof(name)) {
continue;
}
memset(name, 0, sizeof(name));
memset(suffix, 0, sizeof(suffix));
ret = _get_vmname_suffix(entry->d_name,
name, sizeof(name), suffix, sizeof(suffix));
if (ret < 0)
continue;
vm = vmmngr_find(name);
if (!vm) {
vm = calloc(1, sizeof(*vm));
if (!vm) {
printf("%s: Failed to alloc mem for %s\n", __func__, name);
continue;
}
memcpy(vm->name, name, sizeof(vm->name) - 1);
LIST_INSERT_HEAD(&vmmngr_head, vm, list);
}
vm->state_tmp = VM_CREATED;
vm->update = update_count;
}
closedir(dir);
}
static void _remove_dead_vm(void)
{
struct vmmngr_struct *vm, *tvm;
list_foreach_safe(vm, &vmmngr_head, list, tvm) {
if (vm->update == update_count) {
vm->state = vm->state_tmp;
continue;
}
LIST_REMOVE(vm, list);
printf("%s: Removed dead %s\n", __func__, vm->name);
free(vm);
}
};
void vmmngr_update(void)
{
pthread_mutex_lock(&vmmngr_mutex);
update_count++;
_scan_added_vm();
_scan_alive_vm();
_remove_dead_vm();
pthread_mutex_unlock(&vmmngr_mutex);
}
/* helper functions */
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);
if (snprintf(cmd_buf, sizeof(cmd_buf), "%s 2>&1", cmd) >= sizeof(cmd_buf)) {
printf("ERROR: shell command is truncated\n");
return -1;
}
ptr = popen(cmd_buf, "re");
if (!ptr)
return -1;
ret = fread(outbuf, 1, len, ptr);
pclose(ptr);
return ret;
}
static int send_msg(const char *vmname, struct mngr_msg *req,
struct mngr_msg *ack)
{
int fd, ret;
if (!vmname) {
printf("No vmname provided\n");
return -EINVAL;
}
fd = mngr_open_un(vmname, MNGR_CLIENT);
if (fd < 0) {
printf("Unable to open vm %s socket. It may have been shutdown\n", vmname);
return -1;
}
ret = mngr_send_msg(fd, req, ack, 1);
if (ret < 0) {
printf("Unable to send msg to vm %s socket. It may have been shutdown\n", vmname);
mngr_close(fd);
return ret;
}
mngr_close(fd);
return 0;
}
int list_vm()
{
struct vmmngr_struct *s;
int find = 0;
LIST_FOREACH(s, &vmmngr_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;
}
int start_vm(const char *vmname)
{
char cmd[PATH_LEN + sizeof(ACRN_CONF_PATH_ADD) * 2 + MAX_VM_OS_NAME_LEN * 2];
if (snprintf(cmd, sizeof(cmd), "bash %s/%s.sh $(cat %s/%s.args)",
ACRN_CONF_PATH_ADD, vmname, ACRN_CONF_PATH_ADD, vmname) >= sizeof(cmd)) {
printf("ERROR: command is truncated\n");
return -1;
}
return system(cmd);
}
int stop_vm(const char *vmname)
{
struct mngr_msg req;
struct mngr_msg ack;
req.magic = MNGR_MSG_MAGIC;
req.msgid = DM_STOP;
req.timestamp = time(NULL);
send_msg(vmname, &req, &ack);
if (ack.data.err) {
printf("Error happens when try to stop vm. errno(%d)\n",
ack.data.err);
}
return ack.data.err;
}
int pause_vm(const char *vmname)
{
struct mngr_msg req;
struct mngr_msg ack;
req.magic = MNGR_MSG_MAGIC;
req.msgid = DM_PAUSE;
req.timestamp = time(NULL);
send_msg(vmname, &req, &ack);
if (ack.data.err) {
printf("Unable to pause vm. errno(%d)\n", ack.data.err);
}
return ack.data.err;
}
int continue_vm(const char *vmname)
{
struct mngr_msg req;
struct mngr_msg ack;
req.magic = MNGR_MSG_MAGIC;
req.msgid = DM_CONTINUE;
req.timestamp = time(NULL);
send_msg(vmname, &req, &ack);
if (ack.data.err) {
printf("Unable to continue vm. errno(%d)\n", ack.data.err);
}
return ack.data.err;
}
int suspend_vm(const char *vmname)
{
struct mngr_msg req;
struct mngr_msg ack;
req.magic = MNGR_MSG_MAGIC;
req.msgid = DM_SUSPEND;
req.timestamp = time(NULL);
send_msg(vmname, &req, &ack);
if (ack.data.err) {
printf("Unable to suspend vm. errno(%d)\n", ack.data.err);
}
return ack.data.err;
}
int resume_vm(const char *vmname, unsigned reason)
{
struct mngr_msg req;
struct mngr_msg ack;
req.magic = MNGR_MSG_MAGIC;
req.msgid = DM_RESUME;
req.timestamp = time(NULL);
req.data.reason = reason;
send_msg(vmname, &req, &ack);
if (ack.data.err) {
printf("Unable to resume vm. errno(%d)\n", ack.data.err);
}
return ack.data.err;
}