libvirt/src/ch/ch_driver.c
Michal Privoznik 105dace22c Revert "report error when virProcessGetStatInfo() is unable to parse data"
This reverts commit 938382b60ae5bd1f83b5cb09e1ce68b9a88f679a.

Turns out, the commit did more harm than good. It changed
semantics on some public APIs. For instance, while
qemuDomainGetInfo() previously did not returned an error it does
now. While the calls to virProcessGetStatInfo() is guarded with
virDomainObjIsActive() it doesn't necessarily mean that QEMU's
PID is still alive. QEMU might be gone but we just haven't
realized it (e.g. because the eof handler thread is waiting for a
job).

Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=2041610
Signed-off-by: Michal Privoznik <mprivozn@redhat.com>
Reviewed-by: Andrea Bolognani <abologna@redhat.com>
2022-01-20 17:51:07 +01:00

1195 lines
31 KiB
C

/*
* Copyright Intel Corp. 2020-2021
*
* ch_driver.c: Core Cloud-Hypervisor driver functions
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <http://www.gnu.org/licenses/>.
*/
#include <config.h>
#include "ch_conf.h"
#include "ch_domain.h"
#include "ch_driver.h"
#include "ch_monitor.h"
#include "ch_process.h"
#include "datatypes.h"
#include "driver.h"
#include "viraccessapicheck.h"
#include "viralloc.h"
#include "virbuffer.h"
#include "virchrdev.h"
#include "vircommand.h"
#include "virerror.h"
#include "virfile.h"
#include "virlog.h"
#include "virnetdevtap.h"
#include "virobject.h"
#include "virstring.h"
#include "virtypedparam.h"
#include "viruri.h"
#include "virutil.h"
#include "viruuid.h"
#define VIR_FROM_THIS VIR_FROM_CH
VIR_LOG_INIT("ch.ch_driver");
virCHDriver *ch_driver = NULL;
static virDomainObj *
chDomObjFromDomain(virDomain *domain)
{
virDomainObj *vm;
virCHDriver *driver = domain->conn->privateData;
char uuidstr[VIR_UUID_STRING_BUFLEN];
vm = virDomainObjListFindByUUID(driver->domains, domain->uuid);
if (!vm) {
virUUIDFormat(domain->uuid, uuidstr);
virReportError(VIR_ERR_NO_DOMAIN,
_("no domain with matching uuid '%s' (%s)"),
uuidstr, domain->name);
return NULL;
}
return vm;
}
/* Functions */
static int
chConnectURIProbe(char **uri)
{
if (ch_driver == NULL)
return 0;
*uri = g_strdup("ch:///system");
return 1;
}
static virDrvOpenStatus chConnectOpen(virConnectPtr conn,
virConnectAuthPtr auth G_GNUC_UNUSED,
virConf *conf G_GNUC_UNUSED,
unsigned int flags)
{
virCheckFlags(VIR_CONNECT_RO, VIR_DRV_OPEN_ERROR);
/* URI was good, but driver isn't active */
if (ch_driver == NULL) {
virReportError(VIR_ERR_INTERNAL_ERROR,
"%s", _("Cloud-Hypervisor state driver is not active"));
return VIR_DRV_OPEN_ERROR;
}
if (virConnectOpenEnsureACL(conn) < 0)
return VIR_DRV_OPEN_ERROR;
conn->privateData = ch_driver;
return VIR_DRV_OPEN_SUCCESS;
}
static int chConnectClose(virConnectPtr conn)
{
conn->privateData = NULL;
return 0;
}
static const char *chConnectGetType(virConnectPtr conn)
{
if (virConnectGetTypeEnsureACL(conn) < 0)
return NULL;
return "CH";
}
static int chConnectGetVersion(virConnectPtr conn,
unsigned long *version)
{
virCHDriver *driver = conn->privateData;
if (virConnectGetVersionEnsureACL(conn) < 0)
return -1;
chDriverLock(driver);
*version = driver->version;
chDriverUnlock(driver);
return 0;
}
static char *chConnectGetHostname(virConnectPtr conn)
{
if (virConnectGetHostnameEnsureACL(conn) < 0)
return NULL;
return virGetHostname();
}
static int chConnectNumOfDomains(virConnectPtr conn)
{
virCHDriver *driver = conn->privateData;
if (virConnectNumOfDomainsEnsureACL(conn) < 0)
return -1;
return virDomainObjListNumOfDomains(driver->domains, true,
virConnectNumOfDomainsCheckACL, conn);
}
static int chConnectListDomains(virConnectPtr conn, int *ids, int nids)
{
virCHDriver *driver = conn->privateData;
if (virConnectListDomainsEnsureACL(conn) < 0)
return -1;
return virDomainObjListGetActiveIDs(driver->domains, ids, nids,
virConnectListDomainsCheckACL, conn);
}
static int
chConnectListAllDomains(virConnectPtr conn,
virDomainPtr **domains,
unsigned int flags)
{
virCHDriver *driver = conn->privateData;
virCheckFlags(VIR_CONNECT_LIST_DOMAINS_FILTERS_ALL, -1);
if (virConnectListAllDomainsEnsureACL(conn) < 0)
return -1;
return virDomainObjListExport(driver->domains, conn, domains,
virConnectListAllDomainsCheckACL, flags);
}
static int chNodeGetInfo(virConnectPtr conn,
virNodeInfoPtr nodeinfo)
{
if (virNodeGetInfoEnsureACL(conn) < 0)
return -1;
return virCapabilitiesGetNodeInfo(nodeinfo);
}
static char *chConnectGetCapabilities(virConnectPtr conn)
{
virCHDriver *driver = conn->privateData;
g_autoptr(virCaps) caps = NULL;
char *xml;
if (virConnectGetCapabilitiesEnsureACL(conn) < 0)
return NULL;
if (!(caps = virCHDriverGetCapabilities(driver, true)))
return NULL;
xml = virCapabilitiesFormatXML(caps);
return xml;
}
/**
* chDomainCreateXML:
* @conn: pointer to connection
* @xml: XML definition of domain
* @flags: bitwise-OR of supported virDomainCreateFlags
*
* Creates a domain based on xml and starts it
*
* Returns a new domain object or NULL in case of failure.
*/
static virDomainPtr
chDomainCreateXML(virConnectPtr conn,
const char *xml,
unsigned int flags)
{
virCHDriver *driver = conn->privateData;
g_autoptr(virDomainDef) vmdef = NULL;
virDomainObj *vm = NULL;
virDomainPtr dom = NULL;
unsigned int parse_flags = VIR_DOMAIN_DEF_PARSE_INACTIVE;
virCheckFlags(VIR_DOMAIN_START_VALIDATE, NULL);
if (flags & VIR_DOMAIN_START_VALIDATE)
parse_flags |= VIR_DOMAIN_DEF_PARSE_VALIDATE_SCHEMA;
if ((vmdef = virDomainDefParseString(xml, driver->xmlopt,
NULL, parse_flags)) == NULL)
goto cleanup;
if (virDomainCreateXMLEnsureACL(conn, vmdef) < 0)
goto cleanup;
if (!(vm = virDomainObjListAdd(driver->domains,
&vmdef,
driver->xmlopt,
VIR_DOMAIN_OBJ_LIST_ADD_LIVE |
VIR_DOMAIN_OBJ_LIST_ADD_CHECK_LIVE,
NULL)))
goto cleanup;
if (virCHDomainObjBeginJob(vm, CH_JOB_MODIFY) < 0)
goto cleanup;
if (virCHProcessStart(driver, vm, VIR_DOMAIN_RUNNING_BOOTED) < 0)
goto cleanup;
dom = virGetDomain(conn, vm->def->name, vm->def->uuid, vm->def->id);
virCHDomainObjEndJob(vm);
cleanup:
if (vm && !dom) {
virDomainObjListRemove(driver->domains, vm);
}
virDomainObjEndAPI(&vm);
chDriverUnlock(driver);
return dom;
}
static int
chDomainCreateWithFlags(virDomainPtr dom, unsigned int flags)
{
virCHDriver *driver = dom->conn->privateData;
virDomainObj *vm;
int ret = -1;
virCheckFlags(0, -1);
if (!(vm = chDomObjFromDomain(dom)))
goto cleanup;
if (virDomainCreateWithFlagsEnsureACL(dom->conn, vm->def) < 0)
goto cleanup;
if (virCHDomainObjBeginJob(vm, CH_JOB_MODIFY) < 0)
goto cleanup;
ret = virCHProcessStart(driver, vm, VIR_DOMAIN_RUNNING_BOOTED);
virCHDomainObjEndJob(vm);
cleanup:
virDomainObjEndAPI(&vm);
return ret;
}
static int
chDomainCreate(virDomainPtr dom)
{
return chDomainCreateWithFlags(dom, 0);
}
static virDomainPtr
chDomainDefineXMLFlags(virConnectPtr conn, const char *xml, unsigned int flags)
{
virCHDriver *driver = conn->privateData;
g_autoptr(virDomainDef) vmdef = NULL;
virDomainObj *vm = NULL;
virDomainPtr dom = NULL;
unsigned int parse_flags = VIR_DOMAIN_DEF_PARSE_INACTIVE;
virCheckFlags(VIR_DOMAIN_DEFINE_VALIDATE, NULL);
if (flags & VIR_DOMAIN_START_VALIDATE)
parse_flags |= VIR_DOMAIN_DEF_PARSE_VALIDATE_SCHEMA;
if ((vmdef = virDomainDefParseString(xml, driver->xmlopt,
NULL, parse_flags)) == NULL)
goto cleanup;
if (virXMLCheckIllegalChars("name", vmdef->name, "\n") < 0)
goto cleanup;
if (virDomainDefineXMLFlagsEnsureACL(conn, vmdef) < 0)
goto cleanup;
if (!(vm = virDomainObjListAdd(driver->domains, &vmdef,
driver->xmlopt,
0, NULL)))
goto cleanup;
vm->persistent = 1;
dom = virGetDomain(conn, vm->def->name, vm->def->uuid, vm->def->id);
cleanup:
virDomainObjEndAPI(&vm);
return dom;
}
static virDomainPtr
chDomainDefineXML(virConnectPtr conn, const char *xml)
{
return chDomainDefineXMLFlags(conn, xml, 0);
}
static int
chDomainUndefineFlags(virDomainPtr dom,
unsigned int flags)
{
virCHDriver *driver = dom->conn->privateData;
virDomainObj *vm;
int ret = -1;
virCheckFlags(0, -1);
if (!(vm = chDomObjFromDomain(dom)))
goto cleanup;
if (virDomainUndefineFlagsEnsureACL(dom->conn, vm->def) < 0)
goto cleanup;
if (!vm->persistent) {
virReportError(VIR_ERR_OPERATION_INVALID,
"%s", _("Cannot undefine transient domain"));
goto cleanup;
}
if (virDomainObjIsActive(vm)) {
vm->persistent = 0;
} else {
virDomainObjListRemove(driver->domains, vm);
}
ret = 0;
cleanup:
virDomainObjEndAPI(&vm);
return ret;
}
static int
chDomainUndefine(virDomainPtr dom)
{
return chDomainUndefineFlags(dom, 0);
}
static int chDomainIsActive(virDomainPtr dom)
{
virCHDriver *driver = dom->conn->privateData;
virDomainObj *vm;
int ret = -1;
chDriverLock(driver);
if (!(vm = chDomObjFromDomain(dom)))
goto cleanup;
if (virDomainIsActiveEnsureACL(dom->conn, vm->def) < 0)
goto cleanup;
ret = virDomainObjIsActive(vm);
cleanup:
virDomainObjEndAPI(&vm);
chDriverUnlock(driver);
return ret;
}
static int
chDomainShutdownFlags(virDomainPtr dom,
unsigned int flags)
{
virCHDomainObjPrivate *priv;
virDomainObj *vm;
virDomainState state;
int ret = -1;
virCheckFlags(VIR_DOMAIN_SHUTDOWN_ACPI_POWER_BTN, -1);
if (!(vm = chDomObjFromDomain(dom)))
goto cleanup;
priv = vm->privateData;
if (virDomainShutdownFlagsEnsureACL(dom->conn, vm->def, flags) < 0)
goto cleanup;
if (virCHDomainObjBeginJob(vm, CH_JOB_MODIFY) < 0)
goto cleanup;
if (virDomainObjCheckActive(vm) < 0)
goto endjob;
state = virDomainObjGetState(vm, NULL);
if (state != VIR_DOMAIN_RUNNING && state != VIR_DOMAIN_PAUSED) {
virReportError(VIR_ERR_OPERATION_INVALID, "%s",
_("only can shutdown running/paused domain"));
goto endjob;
} else {
if (virCHMonitorShutdownVM(priv->monitor) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("failed to shutdown guest VM"));
goto endjob;
}
}
virDomainObjSetState(vm, VIR_DOMAIN_SHUTDOWN, VIR_DOMAIN_SHUTDOWN_USER);
ret = 0;
endjob:
virCHDomainObjEndJob(vm);
cleanup:
virDomainObjEndAPI(&vm);
return ret;
}
static int
chDomainShutdown(virDomainPtr dom)
{
return chDomainShutdownFlags(dom, 0);
}
static int
chDomainReboot(virDomainPtr dom, unsigned int flags)
{
virCHDomainObjPrivate *priv;
virDomainObj *vm;
virDomainState state;
int ret = -1;
virCheckFlags(VIR_DOMAIN_REBOOT_ACPI_POWER_BTN, -1);
if (!(vm = chDomObjFromDomain(dom)))
goto cleanup;
priv = vm->privateData;
if (virDomainRebootEnsureACL(dom->conn, vm->def, flags) < 0)
goto cleanup;
if (virCHDomainObjBeginJob(vm, CH_JOB_MODIFY) < 0)
goto cleanup;
if (virDomainObjCheckActive(vm) < 0)
goto endjob;
state = virDomainObjGetState(vm, NULL);
if (state != VIR_DOMAIN_RUNNING && state != VIR_DOMAIN_PAUSED) {
virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s",
_("only can reboot running/paused domain"));
goto endjob;
} else {
if (virCHMonitorRebootVM(priv->monitor) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("failed to reboot domain"));
goto endjob;
}
}
if (state == VIR_DOMAIN_RUNNING)
virDomainObjSetState(vm, VIR_DOMAIN_RUNNING, VIR_DOMAIN_RUNNING_BOOTED);
else
virDomainObjSetState(vm, VIR_DOMAIN_RUNNING, VIR_DOMAIN_RUNNING_UNPAUSED);
ret = 0;
endjob:
virCHDomainObjEndJob(vm);
cleanup:
virDomainObjEndAPI(&vm);
return ret;
}
static int
chDomainSuspend(virDomainPtr dom)
{
virCHDomainObjPrivate *priv;
virDomainObj *vm;
int ret = -1;
if (!(vm = chDomObjFromDomain(dom)))
goto cleanup;
priv = vm->privateData;
if (virDomainSuspendEnsureACL(dom->conn, vm->def) < 0)
goto cleanup;
if (virCHDomainObjBeginJob(vm, CH_JOB_MODIFY) < 0)
goto cleanup;
if (virDomainObjCheckActive(vm) < 0)
goto endjob;
if (virDomainObjGetState(vm, NULL) != VIR_DOMAIN_RUNNING) {
virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s",
_("only can suspend running domain"));
goto endjob;
} else {
if (virCHMonitorSuspendVM(priv->monitor) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("failed to suspend domain"));
goto endjob;
}
}
virDomainObjSetState(vm, VIR_DOMAIN_PAUSED, VIR_DOMAIN_PAUSED_USER);
ret = 0;
endjob:
virCHDomainObjEndJob(vm);
cleanup:
virDomainObjEndAPI(&vm);
return ret;
}
static int
chDomainResume(virDomainPtr dom)
{
virCHDomainObjPrivate *priv;
virDomainObj *vm;
int ret = -1;
if (!(vm = chDomObjFromDomain(dom)))
goto cleanup;
priv = vm->privateData;
if (virDomainResumeEnsureACL(dom->conn, vm->def) < 0)
goto cleanup;
if (virCHDomainObjBeginJob(vm, CH_JOB_MODIFY) < 0)
goto cleanup;
if (virDomainObjCheckActive(vm) < 0)
goto endjob;
if (virDomainObjGetState(vm, NULL) != VIR_DOMAIN_PAUSED) {
virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s",
_("only can resume paused domain"));
goto endjob;
} else {
if (virCHMonitorResumeVM(priv->monitor) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("failed to resume domain"));
goto endjob;
}
}
virDomainObjSetState(vm, VIR_DOMAIN_RUNNING, VIR_DOMAIN_RUNNING_UNPAUSED);
ret = 0;
endjob:
virCHDomainObjEndJob(vm);
cleanup:
virDomainObjEndAPI(&vm);
return ret;
}
/**
* chDomainDestroyFlags:
* @dom: pointer to domain to destroy
* @flags: extra flags; not used yet.
*
* Sends SIGKILL to Cloud-Hypervisor process to terminate it
*
* Returns 0 on success or -1 in case of error
*/
static int
chDomainDestroyFlags(virDomainPtr dom, unsigned int flags)
{
virCHDriver *driver = dom->conn->privateData;
virDomainObj *vm;
int ret = -1;
virCheckFlags(0, -1);
if (!(vm = chDomObjFromDomain(dom)))
goto cleanup;
if (virDomainDestroyFlagsEnsureACL(dom->conn, vm->def) < 0)
goto cleanup;
if (virCHDomainObjBeginJob(vm, CH_JOB_DESTROY) < 0)
goto cleanup;
if (virDomainObjCheckActive(vm) < 0)
goto endjob;
ret = virCHProcessStop(driver, vm, VIR_DOMAIN_SHUTOFF_DESTROYED);
endjob:
virCHDomainObjEndJob(vm);
if (!vm->persistent)
virDomainObjListRemove(driver->domains, vm);
cleanup:
virDomainObjEndAPI(&vm);
return ret;
}
static int
chDomainDestroy(virDomainPtr dom)
{
return chDomainDestroyFlags(dom, 0);
}
static virDomainPtr chDomainLookupByID(virConnectPtr conn,
int id)
{
virCHDriver *driver = conn->privateData;
virDomainObj *vm;
virDomainPtr dom = NULL;
chDriverLock(driver);
vm = virDomainObjListFindByID(driver->domains, id);
chDriverUnlock(driver);
if (!vm) {
virReportError(VIR_ERR_NO_DOMAIN,
_("no domain with matching id '%d'"), id);
goto cleanup;
}
if (virDomainLookupByIDEnsureACL(conn, vm->def) < 0)
goto cleanup;
dom = virGetDomain(conn, vm->def->name, vm->def->uuid, vm->def->id);
cleanup:
virDomainObjEndAPI(&vm);
return dom;
}
static virDomainPtr chDomainLookupByName(virConnectPtr conn,
const char *name)
{
virCHDriver *driver = conn->privateData;
virDomainObj *vm;
virDomainPtr dom = NULL;
chDriverLock(driver);
vm = virDomainObjListFindByName(driver->domains, name);
chDriverUnlock(driver);
if (!vm) {
virReportError(VIR_ERR_NO_DOMAIN,
_("no domain with matching name '%s'"), name);
goto cleanup;
}
if (virDomainLookupByNameEnsureACL(conn, vm->def) < 0)
goto cleanup;
dom = virGetDomain(conn, vm->def->name, vm->def->uuid, vm->def->id);
cleanup:
virDomainObjEndAPI(&vm);
return dom;
}
static virDomainPtr chDomainLookupByUUID(virConnectPtr conn,
const unsigned char *uuid)
{
virCHDriver *driver = conn->privateData;
virDomainObj *vm;
virDomainPtr dom = NULL;
chDriverLock(driver);
vm = virDomainObjListFindByUUID(driver->domains, uuid);
chDriverUnlock(driver);
if (!vm) {
char uuidstr[VIR_UUID_STRING_BUFLEN];
virUUIDFormat(uuid, uuidstr);
virReportError(VIR_ERR_NO_DOMAIN,
_("no domain with matching uuid '%s'"), uuidstr);
goto cleanup;
}
if (virDomainLookupByUUIDEnsureACL(conn, vm->def) < 0)
goto cleanup;
dom = virGetDomain(conn, vm->def->name, vm->def->uuid, vm->def->id);
cleanup:
virDomainObjEndAPI(&vm);
return dom;
}
static int
chDomainGetState(virDomainPtr dom,
int *state,
int *reason,
unsigned int flags)
{
virDomainObj *vm;
int ret = -1;
virCheckFlags(0, -1);
if (!(vm = chDomObjFromDomain(dom)))
goto cleanup;
if (virDomainGetStateEnsureACL(dom->conn, vm->def) < 0)
goto cleanup;
*state = virDomainObjGetState(vm, reason);
ret = 0;
cleanup:
virDomainObjEndAPI(&vm);
return ret;
}
static char *chDomainGetXMLDesc(virDomainPtr dom,
unsigned int flags)
{
virCHDriver *driver = dom->conn->privateData;
virDomainObj *vm;
char *ret = NULL;
virCheckFlags(VIR_DOMAIN_XML_COMMON_FLAGS, NULL);
if (!(vm = chDomObjFromDomain(dom)))
goto cleanup;
if (virDomainGetXMLDescEnsureACL(dom->conn, vm->def, flags) < 0)
goto cleanup;
ret = virDomainDefFormat(vm->def, driver->xmlopt,
virDomainDefFormatConvertXMLFlags(flags));
cleanup:
virDomainObjEndAPI(&vm);
return ret;
}
static int chDomainGetInfo(virDomainPtr dom,
virDomainInfoPtr info)
{
virDomainObj *vm;
int ret = -1;
if (!(vm = chDomObjFromDomain(dom)))
goto cleanup;
if (virDomainGetInfoEnsureACL(dom->conn, vm->def) < 0)
goto cleanup;
info->state = virDomainObjGetState(vm, NULL);
info->cpuTime = 0;
info->maxMem = virDomainDefGetMemoryTotal(vm->def);
info->memory = vm->def->mem.cur_balloon;
info->nrVirtCpu = virDomainDefGetVcpus(vm->def);
ret = 0;
cleanup:
virDomainObjEndAPI(&vm);
return ret;
}
static int
chDomainOpenConsole(virDomainPtr dom,
const char *dev_name,
virStreamPtr st,
unsigned int flags)
{
virDomainObj *vm = NULL;
int ret = -1;
size_t i;
virDomainChrDef *chr = NULL;
virCHDomainObjPrivate *priv;
virCheckFlags(VIR_DOMAIN_CONSOLE_SAFE | VIR_DOMAIN_CONSOLE_FORCE, -1);
if (!(vm = chDomObjFromDomain(dom)))
goto cleanup;
if (virDomainOpenConsoleEnsureACL(dom->conn, vm->def) < 0)
goto cleanup;
if (virDomainObjCheckActive(vm) < 0)
goto cleanup;
priv = vm->privateData;
if (dev_name) {
for (i = 0; !chr && i < vm->def->nconsoles; i++) {
if (vm->def->consoles[i]->info.alias &&
STREQ(dev_name, vm->def->consoles[i]->info.alias))
chr = vm->def->consoles[i];
}
for (i = 0; !chr && i < vm->def->nserials; i++) {
if (STREQ(dev_name, vm->def->serials[i]->info.alias))
chr = vm->def->serials[i];
}
} else {
if (vm->def->nconsoles &&
vm->def->consoles[0]->source->type == VIR_DOMAIN_CHR_TYPE_PTY)
chr = vm->def->consoles[0];
else if (vm->def->nserials &&
vm->def->serials[0]->source->type == VIR_DOMAIN_CHR_TYPE_PTY)
chr = vm->def->serials[0];
}
if (!chr) {
virReportError(VIR_ERR_INTERNAL_ERROR, _("cannot find character device %s"),
NULLSTR(dev_name));
goto cleanup;
}
if (chr->source->type != VIR_DOMAIN_CHR_TYPE_PTY) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("character device %s is not using a PTY"),
dev_name ? dev_name : NULLSTR(chr->info.alias));
goto cleanup;
}
/* handle mutually exclusive access to console devices */
ret = virChrdevOpen(priv->chrdevs, chr->source, st,
(flags & VIR_DOMAIN_CONSOLE_FORCE) != 0);
if (ret == 1) {
virReportError(VIR_ERR_OPERATION_FAILED, "%s",
_("Active console session exists for this domain"));
ret = -1;
}
cleanup:
virDomainObjEndAPI(&vm);
return ret;
}
static int chStateCleanup(void)
{
if (ch_driver == NULL)
return -1;
virObjectUnref(ch_driver->domains);
virObjectUnref(ch_driver->xmlopt);
virObjectUnref(ch_driver->caps);
virObjectUnref(ch_driver->config);
virMutexDestroy(&ch_driver->lock);
g_free(ch_driver);
ch_driver = NULL;
return 0;
}
static int chStateInitialize(bool privileged,
const char *root,
virStateInhibitCallback callback G_GNUC_UNUSED,
void *opaque G_GNUC_UNUSED)
{
int ret = VIR_DRV_STATE_INIT_ERROR;
int rv;
if (root != NULL) {
virReportError(VIR_ERR_INVALID_ARG, "%s",
_("Driver does not support embedded mode"));
return -1;
}
ch_driver = g_new0(virCHDriver, 1);
if (virMutexInit(&ch_driver->lock) < 0) {
g_free(ch_driver);
return VIR_DRV_STATE_INIT_ERROR;
}
if (!(ch_driver->domains = virDomainObjListNew()))
goto cleanup;
if (!(ch_driver->caps = virCHDriverCapsInit()))
goto cleanup;
if (!(ch_driver->xmlopt = chDomainXMLConfInit(ch_driver)))
goto cleanup;
if (!(ch_driver->config = virCHDriverConfigNew(privileged)))
goto cleanup;
if ((rv = chExtractVersion(ch_driver)) < 0) {
if (rv == -2)
ret = VIR_DRV_STATE_INIT_SKIPPED;
goto cleanup;
}
ch_driver->privileged = privileged;
ret = VIR_DRV_STATE_INIT_COMPLETE;
cleanup:
if (ret != VIR_DRV_STATE_INIT_COMPLETE)
chStateCleanup();
return ret;
}
static int
chDomainGetVcpusFlags(virDomainPtr dom,
unsigned int flags)
{
virDomainObj *vm;
virDomainDef *def;
int ret = -1;
virCheckFlags(VIR_DOMAIN_AFFECT_LIVE |
VIR_DOMAIN_AFFECT_CONFIG |
VIR_DOMAIN_VCPU_MAXIMUM | VIR_DOMAIN_VCPU_GUEST, -1);
if (!(vm = chDomObjFromDomain(dom)))
return -1;
if (virDomainGetVcpusFlagsEnsureACL(dom->conn, vm->def, flags) < 0)
goto cleanup;
if (!(def = virDomainObjGetOneDef(vm, flags)))
goto cleanup;
if (flags & VIR_DOMAIN_VCPU_MAXIMUM)
ret = virDomainDefGetVcpusMax(def);
else
ret = virDomainDefGetVcpus(def);
cleanup:
virDomainObjEndAPI(&vm);
return ret;
}
static int
chDomainGetMaxVcpus(virDomainPtr dom)
{
return chDomainGetVcpusFlags(dom,
(VIR_DOMAIN_AFFECT_LIVE |
VIR_DOMAIN_VCPU_MAXIMUM));
}
static int
chDomainGetVcpuPinInfo(virDomain *dom,
int ncpumaps,
unsigned char *cpumaps,
int maplen,
unsigned int flags)
{
virDomainObj *vm = NULL;
virDomainDef *def;
bool live;
int ret = -1;
g_autoptr(virBitmap) hostcpus = NULL;
virBitmap *autoCpuset = NULL;
virCheckFlags(VIR_DOMAIN_AFFECT_LIVE | VIR_DOMAIN_AFFECT_CONFIG, -1);
if (!(vm = chDomObjFromDomain(dom)))
goto cleanup;
if (virDomainGetVcpuPinInfoEnsureACL(dom->conn, vm->def) < 0)
goto cleanup;
if (!(def = virDomainObjGetOneDefState(vm, flags, &live)))
goto cleanup;
if (!(hostcpus = virHostCPUGetAvailableCPUsBitmap()))
goto cleanup;
if (live)
autoCpuset = CH_DOMAIN_PRIVATE(vm)->autoCpuset;
ret = virDomainDefGetVcpuPinInfoHelper(def, maplen, ncpumaps, cpumaps,
hostcpus, autoCpuset);
cleanup:
virDomainObjEndAPI(&vm);
return ret;
}
static int
chNodeGetCPUMap(virConnectPtr conn,
unsigned char **cpumap,
unsigned int *online, unsigned int flags)
{
if (virNodeGetCPUMapEnsureACL(conn) < 0)
return -1;
return virHostCPUGetMap(cpumap, online, flags);
}
static int
chDomainHelperGetVcpus(virDomainObj *vm,
virVcpuInfoPtr info,
unsigned long long *cpuwait,
int maxinfo,
unsigned char *cpumaps,
int maplen)
{
size_t ncpuinfo = 0;
size_t i;
if (maxinfo == 0)
return 0;
if (!virCHDomainHasVcpuPids(vm)) {
virReportError(VIR_ERR_OPERATION_INVALID,
"%s", _("cpu affinity is not supported"));
return -1;
}
if (info)
memset(info, 0, sizeof(*info) * maxinfo);
if (cpumaps)
memset(cpumaps, 0, sizeof(*cpumaps) * maxinfo);
for (i = 0; i < virDomainDefGetVcpusMax(vm->def) && ncpuinfo < maxinfo; i++) {
virDomainVcpuDef *vcpu = virDomainDefGetVcpu(vm->def, i);
pid_t vcpupid = virCHDomainGetVcpuPid(vm, i);
virVcpuInfoPtr vcpuinfo = info + ncpuinfo;
if (!vcpu->online)
continue;
if (info) {
vcpuinfo->number = i;
vcpuinfo->state = VIR_VCPU_RUNNING;
if (virProcessGetStatInfo(&vcpuinfo->cpuTime,
&vcpuinfo->cpu, NULL,
vm->pid, vcpupid) < 0) {
virReportSystemError(errno, "%s",
_("cannot get vCPU placement & pCPU time"));
return -1;
}
}
if (cpumaps) {
unsigned char *cpumap = VIR_GET_CPUMAP(cpumaps, maplen, ncpuinfo);
g_autoptr(virBitmap) map = NULL;
if (!(map = virProcessGetAffinity(vcpupid)))
return -1;
virBitmapToDataBuf(map, cpumap, maplen);
}
if (cpuwait) {
if (virProcessGetSchedInfo(&(cpuwait[ncpuinfo]), vm->pid, vcpupid) < 0)
return -1;
}
ncpuinfo++;
}
return ncpuinfo;
}
static int
chDomainGetVcpus(virDomainPtr dom,
virVcpuInfoPtr info,
int maxinfo,
unsigned char *cpumaps,
int maplen)
{
virDomainObj *vm;
int ret = -1;
if (!(vm = chDomObjFromDomain(dom)))
goto cleanup;
if (virDomainGetVcpusEnsureACL(dom->conn, vm->def) < 0)
goto cleanup;
if (virDomainObjCheckActive(vm) < 0) {
virReportError(VIR_ERR_OPERATION_INVALID, "%s",
_("cannot retrieve vcpu information for inactive domain"));
goto cleanup;
}
ret = chDomainHelperGetVcpus(vm, info, NULL, maxinfo, cpumaps, maplen);
cleanup:
virDomainObjEndAPI(&vm);
return ret;
}
/* Function Tables */
static virHypervisorDriver chHypervisorDriver = {
.name = "CH",
.connectURIProbe = chConnectURIProbe,
.connectOpen = chConnectOpen, /* 7.5.0 */
.connectClose = chConnectClose, /* 7.5.0 */
.connectGetType = chConnectGetType, /* 7.5.0 */
.connectGetVersion = chConnectGetVersion, /* 7.5.0 */
.connectGetHostname = chConnectGetHostname, /* 7.5.0 */
.connectNumOfDomains = chConnectNumOfDomains, /* 7.5.0 */
.connectListAllDomains = chConnectListAllDomains, /* 7.5.0 */
.connectListDomains = chConnectListDomains, /* 7.5.0 */
.connectGetCapabilities = chConnectGetCapabilities, /* 7.5.0 */
.domainCreateXML = chDomainCreateXML, /* 7.5.0 */
.domainCreate = chDomainCreate, /* 7.5.0 */
.domainCreateWithFlags = chDomainCreateWithFlags, /* 7.5.0 */
.domainShutdown = chDomainShutdown, /* 7.5.0 */
.domainShutdownFlags = chDomainShutdownFlags, /* 7.5.0 */
.domainReboot = chDomainReboot, /* 7.5.0 */
.domainSuspend = chDomainSuspend, /* 7.5.0 */
.domainResume = chDomainResume, /* 7.5.0 */
.domainDestroy = chDomainDestroy, /* 7.5.0 */
.domainDestroyFlags = chDomainDestroyFlags, /* 7.5.0 */
.domainDefineXML = chDomainDefineXML, /* 7.5.0 */
.domainDefineXMLFlags = chDomainDefineXMLFlags, /* 7.5.0 */
.domainUndefine = chDomainUndefine, /* 7.5.0 */
.domainUndefineFlags = chDomainUndefineFlags, /* 7.5.0 */
.domainLookupByID = chDomainLookupByID, /* 7.5.0 */
.domainLookupByUUID = chDomainLookupByUUID, /* 7.5.0 */
.domainLookupByName = chDomainLookupByName, /* 7.5.0 */
.domainGetState = chDomainGetState, /* 7.5.0 */
.domainGetXMLDesc = chDomainGetXMLDesc, /* 7.5.0 */
.domainGetInfo = chDomainGetInfo, /* 7.5.0 */
.domainIsActive = chDomainIsActive, /* 7.5.0 */
.domainOpenConsole = chDomainOpenConsole, /* 7.8.0 */
.nodeGetInfo = chNodeGetInfo, /* 7.5.0 */
.domainGetVcpus = chDomainGetVcpus, /* 8.0.0 */
.domainGetVcpusFlags = chDomainGetVcpusFlags, /* 8.0.0 */
.domainGetMaxVcpus = chDomainGetMaxVcpus, /* 8.0.0 */
.domainGetVcpuPinInfo = chDomainGetVcpuPinInfo, /* 8.0.0 */
.nodeGetCPUMap = chNodeGetCPUMap, /* 8.0.0 */
};
static virConnectDriver chConnectDriver = {
.localOnly = true,
.uriSchemes = (const char *[]){"ch", NULL},
.hypervisorDriver = &chHypervisorDriver,
};
static virStateDriver chStateDriver = {
.name = "cloud-hypervisor",
.stateInitialize = chStateInitialize,
.stateCleanup = chStateCleanup,
};
int chRegister(void)
{
if (virRegisterConnectDriver(&chConnectDriver, true) < 0)
return -1;
if (virRegisterStateDriver(&chStateDriver) < 0)
return -1;
return 0;
}