libvirt/src/bhyve/bhyve_driver.c

1718 lines
47 KiB
C
Raw Normal View History

/*
* bhyve_driver.c: core driver methods for managing bhyve guests
*
* Copyright (C) 2014 Roman Bogorodskiy
* Copyright (C) 2014-2015 Red Hat, Inc.
*
* 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 <fcntl.h>
#include <sys/utsname.h>
#include "virerror.h"
#include "datatypes.h"
#include "virbuffer.h"
#include "viruuid.h"
#include "configmake.h"
#include "viralloc.h"
#include "network_conf.h"
#include "interface_conf.h"
#include "domain_audit.h"
#include "domain_event.h"
#include "snapshot_conf.h"
#include "virfdstream.h"
#include "storage_conf.h"
#include "node_device_conf.h"
#include "virdomainobjlist.h"
#include "virxml.h"
#include "virthread.h"
#include "virlog.h"
#include "virfile.h"
#include "virpidfile.h"
#include "virtypedparam.h"
#include "virrandom.h"
#include "virstring.h"
#include "cpu/cpu.h"
#include "viraccessapicheck.h"
#include "virhostcpu.h"
#include "virhostmem.h"
#include "virportallocator.h"
#include "conf/domain_capabilities.h"
#include "bhyve_conf.h"
#include "bhyve_device.h"
#include "bhyve_driver.h"
#include "bhyve_command.h"
#include "bhyve_parse_command.h"
#include "bhyve_domain.h"
#include "bhyve_process.h"
#include "bhyve_capabilities.h"
#define VIR_FROM_THIS VIR_FROM_BHYVE
VIR_LOG_INIT("bhyve.bhyve_driver");
bhyveConnPtr bhyve_driver = NULL;
void
bhyveDriverLock(bhyveConnPtr driver)
{
virMutexLock(&driver->lock);
}
void
bhyveDriverUnlock(bhyveConnPtr driver)
{
virMutexUnlock(&driver->lock);
}
static int
bhyveAutostartDomain(virDomainObjPtr vm, void *opaque)
{
const struct bhyveAutostartData *data = opaque;
int ret = 0;
virObjectLock(vm);
if (vm->autostart && !virDomainObjIsActive(vm)) {
virResetLastError();
ret = virBhyveProcessStart(data->conn, data->driver, vm,
VIR_DOMAIN_RUNNING_BOOTED, 0);
if (ret < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Failed to autostart VM '%s': %s"),
vm->def->name, virGetLastErrorMessage());
}
}
virObjectUnlock(vm);
return ret;
}
static void
bhyveAutostartDomains(bhyveConnPtr driver)
{
/* XXX: Figure out a better way todo this. The domain
* startup code needs a connection handle in order
* to lookup the bridge associated with a virtual
* network
*/
virConnectPtr conn = virConnectOpen("bhyve:///system");
/* Ignoring NULL conn which is mostly harmless here */
struct bhyveAutostartData data = { driver, conn };
virDomainObjListForEach(driver->domains, false, bhyveAutostartDomain, &data);
virObjectUnref(conn);
}
/**
* bhyveDriverGetCapabilities:
*
* Get a reference to the virCapsPtr instance for the
* driver.
*
* The caller must release the reference with virObjetUnref
*
* Returns: a reference to a virCapsPtr instance or NULL
*/
virCapsPtr ATTRIBUTE_NONNULL(1)
bhyveDriverGetCapabilities(bhyveConnPtr driver)
{
return virObjectRef(driver->caps);
}
static char *
bhyveConnectGetCapabilities(virConnectPtr conn)
{
bhyveConnPtr privconn = conn->privateData;
virCapsPtr caps;
char *xml = NULL;
if (virConnectGetCapabilitiesEnsureACL(conn) < 0)
return NULL;
if (!(caps = bhyveDriverGetCapabilities(privconn))) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Unable to get Capabilities"));
goto cleanup;
}
if (!(xml = virCapabilitiesFormatXML(caps)))
goto cleanup;
cleanup:
virObjectUnref(caps);
return xml;
}
static virDomainObjPtr
bhyveDomObjFromDomain(virDomainPtr domain)
{
virDomainObjPtr vm;
bhyveConnPtr privconn = domain->conn->privateData;
char uuidstr[VIR_UUID_STRING_BUFLEN];
vm = virDomainObjListFindByUUID(privconn->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;
}
static int
bhyveConnectURIProbe(char **uri)
{
if (bhyve_driver == NULL)
return 0;
*uri = g_strdup("bhyve:///system");
return 1;
}
static virDrvOpenStatus
bhyveConnectOpen(virConnectPtr conn,
virConnectAuthPtr auth G_GNUC_UNUSED,
virConfPtr conf G_GNUC_UNUSED,
unsigned int flags)
{
virCheckFlags(VIR_CONNECT_RO, VIR_DRV_OPEN_ERROR);
if (STRNEQ(conn->uri->path, "/system")) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Unexpected bhyve URI path '%s', try bhyve:///system"),
conn->uri->path);
return VIR_DRV_OPEN_ERROR;
}
if (bhyve_driver == NULL) {
virReportError(VIR_ERR_INTERNAL_ERROR,
"%s", _("bhyve state driver is not active"));
return VIR_DRV_OPEN_ERROR;
}
if (virConnectOpenEnsureACL(conn) < 0)
return VIR_DRV_OPEN_ERROR;
conn->privateData = bhyve_driver;
return VIR_DRV_OPEN_SUCCESS;
}
static int
bhyveConnectClose(virConnectPtr conn)
{
bhyveConnPtr privconn = conn->privateData;
virCloseCallbacksRun(privconn->closeCallbacks, conn, privconn->domains, privconn);
conn->privateData = NULL;
return 0;
}
static char *
bhyveConnectGetHostname(virConnectPtr conn)
{
if (virConnectGetHostnameEnsureACL(conn) < 0)
return NULL;
return virGetHostname();
}
2014-04-23 09:24:05 +00:00
static char *
bhyveConnectGetSysinfo(virConnectPtr conn, unsigned int flags)
{
bhyveConnPtr privconn = conn->privateData;
virBuffer buf = VIR_BUFFER_INITIALIZER;
virCheckFlags(0, NULL);
if (virConnectGetSysinfoEnsureACL(conn) < 0)
return NULL;
if (!privconn->hostsysinfo) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("Host SMBIOS information is not available"));
return NULL;
}
if (virSysinfoFormat(&buf, privconn->hostsysinfo) < 0)
return NULL;
return virBufferContentAndReset(&buf);
}
static int
bhyveConnectGetVersion(virConnectPtr conn, unsigned long *version)
{
struct utsname ver;
if (virConnectGetVersionEnsureACL(conn) < 0)
return -1;
uname(&ver);
if (virParseVersionString(ver.release, version, true) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Unknown release: %s"), ver.release);
return -1;
}
return 0;
}
static int
bhyveDomainGetInfo(virDomainPtr domain, virDomainInfoPtr info)
{
virDomainObjPtr vm;
int ret = -1;
if (!(vm = bhyveDomObjFromDomain(domain)))
goto cleanup;
if (virDomainGetInfoEnsureACL(domain->conn, vm->def) < 0)
goto cleanup;
if (virDomainObjIsActive(vm)) {
if (virBhyveGetDomainTotalCpuStats(vm, &(info->cpuTime)) < 0)
goto cleanup;
} else {
info->cpuTime = 0;
}
info->state = virDomainObjGetState(vm, NULL);
info->maxMem = virDomainDefGetMemoryTotal(vm->def);
info->nrVirtCpu = virDomainDefGetVcpus(vm->def);
ret = 0;
cleanup:
virDomainObjEndAPI(&vm);
return ret;
}
static int
bhyveDomainGetState(virDomainPtr domain,
int *state,
int *reason,
unsigned int flags)
{
virDomainObjPtr vm;
int ret = -1;
virCheckFlags(0, -1);
if (!(vm = bhyveDomObjFromDomain(domain)))
goto cleanup;
if (virDomainGetStateEnsureACL(domain->conn, vm->def) < 0)
goto cleanup;
*state = virDomainObjGetState(vm, reason);
ret = 0;
cleanup:
virDomainObjEndAPI(&vm);
return ret;
}
static int
bhyveDomainGetAutostart(virDomainPtr domain, int *autostart)
{
virDomainObjPtr vm;
int ret = -1;
if (!(vm = bhyveDomObjFromDomain(domain)))
goto cleanup;
if (virDomainGetAutostartEnsureACL(domain->conn, vm->def) < 0)
goto cleanup;
*autostart = vm->autostart;
ret = 0;
cleanup:
virDomainObjEndAPI(&vm);
return ret;
}
static int
bhyveDomainSetAutostart(virDomainPtr domain, int autostart)
{
virDomainObjPtr vm;
char *configFile = NULL;
char *autostartLink = NULL;
int ret = -1;
if (!(vm = bhyveDomObjFromDomain(domain)))
goto cleanup;
if (virDomainSetAutostartEnsureACL(domain->conn, vm->def) < 0)
goto cleanup;
if (!vm->persistent) {
virReportError(VIR_ERR_OPERATION_INVALID, "%s",
_("cannot set autostart for transient domain"));
goto cleanup;
}
autostart = (autostart != 0);
if (vm->autostart != autostart) {
if ((configFile = virDomainConfigFile(BHYVE_CONFIG_DIR, vm->def->name)) == NULL)
goto cleanup;
if ((autostartLink = virDomainConfigFile(BHYVE_AUTOSTART_DIR, vm->def->name)) == NULL)
goto cleanup;
if (autostart) {
if (virFileMakePath(BHYVE_AUTOSTART_DIR) < 0) {
virReportSystemError(errno,
_("cannot create autostart directory %s"),
BHYVE_AUTOSTART_DIR);
goto cleanup;
}
if (symlink(configFile, autostartLink) < 0) {
virReportSystemError(errno,
_("Failed to create symlink '%s' to '%s'"),
autostartLink, configFile);
goto cleanup;
}
} else {
if (unlink(autostartLink) < 0 && errno != ENOENT && errno != ENOTDIR) {
virReportSystemError(errno,
_("Failed to delete symlink '%s'"),
autostartLink);
goto cleanup;
}
}
vm->autostart = autostart;
}
ret = 0;
cleanup:
VIR_FREE(configFile);
VIR_FREE(autostartLink);
virDomainObjEndAPI(&vm);
return ret;
}
static int
bhyveDomainIsActive(virDomainPtr domain)
{
virDomainObjPtr obj;
int ret = -1;
if (!(obj = bhyveDomObjFromDomain(domain)))
goto cleanup;
if (virDomainIsActiveEnsureACL(domain->conn, obj->def) < 0)
goto cleanup;
ret = virDomainObjIsActive(obj);
cleanup:
virDomainObjEndAPI(&obj);
return ret;
}
static int
bhyveDomainIsPersistent(virDomainPtr domain)
{
virDomainObjPtr obj;
int ret = -1;
if (!(obj = bhyveDomObjFromDomain(domain)))
goto cleanup;
if (virDomainIsPersistentEnsureACL(domain->conn, obj->def) < 0)
goto cleanup;
ret = obj->persistent;
cleanup:
virDomainObjEndAPI(&obj);
return ret;
}
2015-10-20 17:32:42 +00:00
static char *
bhyveDomainGetOSType(virDomainPtr dom)
{
virDomainObjPtr vm;
char *ret = NULL;
if (!(vm = bhyveDomObjFromDomain(dom)))
goto cleanup;
if (virDomainGetOSTypeEnsureACL(dom->conn, vm->def) < 0)
goto cleanup;
ret = g_strdup(virDomainOSTypeToString(vm->def->os.type));
2015-10-20 17:32:42 +00:00
cleanup:
virDomainObjEndAPI(&vm);
2015-10-20 17:32:42 +00:00
return ret;
}
static char *
bhyveDomainGetXMLDesc(virDomainPtr domain, unsigned int flags)
{
bhyveConnPtr privconn = domain->conn->privateData;
virDomainObjPtr vm;
char *ret = NULL;
domain: Fix unknown flags diagnosis in virDomainGetXMLDesc Many drivers had a comment that they did not validate the incoming 'flags' to virDomainGetXMLDesc() because they were relying on virDomainDefFormat() to do it instead. This used to be the case (at least since 461e0f1a and friends in 0.9.4 added unknown flag checking in general), but regressed in commit 0ecd6851 (1.2.12), when all of the drivers were changed to pass 'flags' through the new helper virDomainDefFormatConvertXMLFlags(). Since this helper silently ignores unknown flags, we need to implement flag checking in each driver instead. Annoyingly, this means that any new flag values added will silently be ignored when targeting an older libvirt, rather than our usual practice of loudly diagnosing an unsupported flag. Add comments in domain_conf.[ch] to remind us to be extra vigilant about the impact when adding flags (a new flag to add data is safe if the older server omitting the requested data doesn't break things in the newer client; a new flag to suppress data rather than enhancing the existing VIR_DOMAIN_XML_SECURE may form a data leak or even a security hole). In the qemu driver, there are multiple callers all funnelling to qemuDomainDefFormatBufInternal(); many of them already validated flags (and often only a subset of the full set of possible flags), but for ease of maintenance, we can also check flags at the common helper function. Signed-off-by: Eric Blake <eblake@redhat.com> Reviewed-by: John Ferlan <jferlan@redhat.com>
2019-02-14 20:25:01 +00:00
virCheckFlags(VIR_DOMAIN_XML_COMMON_FLAGS, NULL);
if (!(vm = bhyveDomObjFromDomain(domain)))
goto cleanup;
if (virDomainGetXMLDescEnsureACL(domain->conn, vm->def, flags) < 0)
goto cleanup;
ret = virDomainDefFormat(vm->def, privconn->xmlopt,
virDomainDefFormatConvertXMLFlags(flags));
cleanup:
virDomainObjEndAPI(&vm);
return ret;
}
static virDomainPtr
bhyveDomainDefineXMLFlags(virConnectPtr conn, const char *xml, unsigned int flags)
{
bhyveConnPtr privconn = conn->privateData;
virDomainPtr dom = NULL;
virDomainDefPtr def = NULL;
virDomainDefPtr oldDef = NULL;
virDomainObjPtr vm = NULL;
virObjectEventPtr event = NULL;
virCapsPtr caps = NULL;
unsigned int parse_flags = VIR_DOMAIN_DEF_PARSE_INACTIVE;
virCheckFlags(VIR_DOMAIN_DEFINE_VALIDATE, NULL);
if (flags & VIR_DOMAIN_DEFINE_VALIDATE)
parse_flags |= VIR_DOMAIN_DEF_PARSE_VALIDATE_SCHEMA;
caps = bhyveDriverGetCapabilities(privconn);
if (!caps)
return NULL;
if ((def = virDomainDefParseString(xml, privconn->xmlopt,
NULL, parse_flags)) == NULL)
goto cleanup;
if (virXMLCheckIllegalChars("name", def->name, "\n") < 0)
goto cleanup;
if (virDomainDefineXMLFlagsEnsureACL(conn, def) < 0)
goto cleanup;
if (bhyveDomainAssignAddresses(def, NULL) < 0)
goto cleanup;
if (!(vm = virDomainObjListAdd(privconn->domains, def,
privconn->xmlopt,
0, &oldDef)))
goto cleanup;
def = NULL;
vm->persistent = 1;
if (virDomainDefSave(vm->newDef ? vm->newDef : vm->def,
privconn->xmlopt, BHYVE_CONFIG_DIR) < 0) {
virDomainObjListRemove(privconn->domains, vm);
goto cleanup;
}
event = virDomainEventLifecycleNewFromObj(vm,
VIR_DOMAIN_EVENT_DEFINED,
!oldDef ?
VIR_DOMAIN_EVENT_DEFINED_ADDED :
VIR_DOMAIN_EVENT_DEFINED_UPDATED);
dom = virGetDomain(conn, vm->def->name, vm->def->uuid, vm->def->id);
cleanup:
virObjectUnref(caps);
virDomainDefFree(def);
virDomainDefFree(oldDef);
conf: Clean up object referencing for Add and Remove When adding a new object to the domain object list, there should have been 2 virObjectRef calls made one for each list into which the object was placed to match the 2 virObjectUnref calls that would occur during Remove as part of virHashRemoveEntry when virObjectFreeHashData is called when the element is removed from the hash table as set up in virDomainObjListNew. Some drivers (libxl, lxc, qemu, and vz) handled this inconsistency by calling virObjectRef upon successful return from virDomainObjListAdd in order to use virDomainObjEndAPI when done with the returned @vm. While others (bhyve, openvz, test, and vmware) handled this via only calling virObjectUnlock upon successful return from virDomainObjListAdd. This patch will "unify" the approach to use virDomainObjEndAPI for any @vm successfully returned from virDomainObjListAdd. Because list removal is so tightly coupled with list addition, this patch fixes the list removal algorithm to return the object as entered - "locked and reffed". This way, the callers can then decide how to uniformly handle add/remove success and failure. This removes the onus on the caller to "specially handle" the @vm during removal processing. The Add/Remove logic allows for some logic simplification such as in libxl where we can Remove the @vm directly rather than needing to set a @remove_dom boolean and removing after the libxlDomainObjEndJob completes as the @vm is locked/reffed. Signed-off-by: John Ferlan <jferlan@redhat.com> Reviewed-by: Erik Skultety <eskultet@redhat.com>
2018-04-23 14:40:48 +00:00
virDomainObjEndAPI(&vm);
virObjectEventStateQueue(privconn->domainEventState, event);
return dom;
}
static virDomainPtr
bhyveDomainDefineXML(virConnectPtr conn, const char *xml)
{
return bhyveDomainDefineXMLFlags(conn, xml, 0);
}
static int
bhyveDomainUndefineFlags(virDomainPtr domain, unsigned int flags)
{
bhyveConnPtr privconn = domain->conn->privateData;
virObjectEventPtr event = NULL;
virDomainObjPtr vm;
int ret = -1;
virCheckFlags(0, -1);
if (!(vm = bhyveDomObjFromDomain(domain)))
goto cleanup;
if (virDomainUndefineFlagsEnsureACL(domain->conn, vm->def) < 0)
goto cleanup;
if (!vm->persistent) {
virReportError(VIR_ERR_OPERATION_INVALID,
"%s", _("Cannot undefine transient domain"));
goto cleanup;
}
if (virDomainDeleteConfig(BHYVE_CONFIG_DIR,
BHYVE_AUTOSTART_DIR,
vm) < 0)
goto cleanup;
event = virDomainEventLifecycleNewFromObj(vm,
VIR_DOMAIN_EVENT_UNDEFINED,
VIR_DOMAIN_EVENT_UNDEFINED_REMOVED);
conf: Clean up object referencing for Add and Remove When adding a new object to the domain object list, there should have been 2 virObjectRef calls made one for each list into which the object was placed to match the 2 virObjectUnref calls that would occur during Remove as part of virHashRemoveEntry when virObjectFreeHashData is called when the element is removed from the hash table as set up in virDomainObjListNew. Some drivers (libxl, lxc, qemu, and vz) handled this inconsistency by calling virObjectRef upon successful return from virDomainObjListAdd in order to use virDomainObjEndAPI when done with the returned @vm. While others (bhyve, openvz, test, and vmware) handled this via only calling virObjectUnlock upon successful return from virDomainObjListAdd. This patch will "unify" the approach to use virDomainObjEndAPI for any @vm successfully returned from virDomainObjListAdd. Because list removal is so tightly coupled with list addition, this patch fixes the list removal algorithm to return the object as entered - "locked and reffed". This way, the callers can then decide how to uniformly handle add/remove success and failure. This removes the onus on the caller to "specially handle" the @vm during removal processing. The Add/Remove logic allows for some logic simplification such as in libxl where we can Remove the @vm directly rather than needing to set a @remove_dom boolean and removing after the libxlDomainObjEndJob completes as the @vm is locked/reffed. Signed-off-by: John Ferlan <jferlan@redhat.com> Reviewed-by: Erik Skultety <eskultet@redhat.com>
2018-04-23 14:40:48 +00:00
if (virDomainObjIsActive(vm))
vm->persistent = 0;
conf: Clean up object referencing for Add and Remove When adding a new object to the domain object list, there should have been 2 virObjectRef calls made one for each list into which the object was placed to match the 2 virObjectUnref calls that would occur during Remove as part of virHashRemoveEntry when virObjectFreeHashData is called when the element is removed from the hash table as set up in virDomainObjListNew. Some drivers (libxl, lxc, qemu, and vz) handled this inconsistency by calling virObjectRef upon successful return from virDomainObjListAdd in order to use virDomainObjEndAPI when done with the returned @vm. While others (bhyve, openvz, test, and vmware) handled this via only calling virObjectUnlock upon successful return from virDomainObjListAdd. This patch will "unify" the approach to use virDomainObjEndAPI for any @vm successfully returned from virDomainObjListAdd. Because list removal is so tightly coupled with list addition, this patch fixes the list removal algorithm to return the object as entered - "locked and reffed". This way, the callers can then decide how to uniformly handle add/remove success and failure. This removes the onus on the caller to "specially handle" the @vm during removal processing. The Add/Remove logic allows for some logic simplification such as in libxl where we can Remove the @vm directly rather than needing to set a @remove_dom boolean and removing after the libxlDomainObjEndJob completes as the @vm is locked/reffed. Signed-off-by: John Ferlan <jferlan@redhat.com> Reviewed-by: Erik Skultety <eskultet@redhat.com>
2018-04-23 14:40:48 +00:00
else
virDomainObjListRemove(privconn->domains, vm);
ret = 0;
cleanup:
virDomainObjEndAPI(&vm);
virObjectEventStateQueue(privconn->domainEventState, event);
return ret;
}
static int
bhyveDomainUndefine(virDomainPtr domain)
{
return bhyveDomainUndefineFlags(domain, 0);
}
static int
bhyveConnectListDomains(virConnectPtr conn, int *ids, int maxids)
{
bhyveConnPtr privconn = conn->privateData;
if (virConnectListDomainsEnsureACL(conn) < 0)
return -1;
return virDomainObjListGetActiveIDs(privconn->domains, ids, maxids,
virConnectListDomainsCheckACL, conn);
}
static int
bhyveConnectNumOfDomains(virConnectPtr conn)
{
bhyveConnPtr privconn = conn->privateData;
if (virConnectNumOfDomainsEnsureACL(conn) < 0)
return -1;
return virDomainObjListNumOfDomains(privconn->domains, true,
virConnectNumOfDomainsCheckACL, conn);
}
static int
bhyveConnectListDefinedDomains(virConnectPtr conn, char **const names,
int maxnames)
{
bhyveConnPtr privconn = conn->privateData;
if (virConnectListDefinedDomainsEnsureACL(conn) < 0)
return -1;
memset(names, 0, sizeof(*names) * maxnames);
return virDomainObjListGetInactiveNames(privconn->domains, names,
maxnames,
virConnectListDefinedDomainsCheckACL,
conn);
}
static int
bhyveConnectNumOfDefinedDomains(virConnectPtr conn)
{
bhyveConnPtr privconn = conn->privateData;
if (virConnectNumOfDefinedDomainsEnsureACL(conn) < 0)
return -1;
return virDomainObjListNumOfDomains(privconn->domains, false,
virConnectNumOfDefinedDomainsCheckACL,
conn);
}
static char *
bhyveConnectDomainXMLToNative(virConnectPtr conn,
const char *format,
const char *xmlData,
unsigned int flags)
{
virBuffer buf = VIR_BUFFER_INITIALIZER;
bhyveConnPtr privconn = conn->privateData;
virDomainDefPtr def = NULL;
virCommandPtr cmd = NULL, loadcmd = NULL;
char *ret = NULL;
virCheckFlags(0, NULL);
if (virConnectDomainXMLToNativeEnsureACL(conn) < 0)
goto cleanup;
if (STRNEQ(format, BHYVE_CONFIG_FORMAT_ARGV)) {
virReportError(VIR_ERR_INVALID_ARG,
_("Unsupported config type %s"), format);
goto cleanup;
}
if (!(def = virDomainDefParseString(xmlData, privconn->xmlopt,
NULL, VIR_DOMAIN_DEF_PARSE_INACTIVE)))
goto cleanup;
if (bhyveDomainAssignAddresses(def, NULL) < 0)
goto cleanup;
if (def->os.bootloader == NULL &&
def->os.loader) {
if (!virDomainDefHasOldStyleROUEFI(def)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("Only read-only pflash is supported."));
goto cleanup;
}
if ((bhyveDriverGetBhyveCaps(privconn) & BHYVE_CAP_LPC_BOOTROM) == 0) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("Installed bhyve binary does not support "
"bootrom"));
goto cleanup;
}
} else {
if (!(loadcmd = virBhyveProcessBuildLoadCmd(privconn, def,
"<device.map>", NULL)))
goto cleanup;
qemu: use line breaks in command line args written to log The QEMU command line arguments are very long and currently all written on a single line to /var/log/libvirt/qemu/$GUEST.log. This introduces logic to add line breaks after every env variable and "-" optional argument, and every positional argument. This will create a clearer log file, which will in turn present better in bug reports when people cut + paste from the log into a bug comment. An example log file entry now looks like this: 2018-12-14 12:57:03.677+0000: starting up libvirt version: 5.0.0, qemu version: 3.0.0qemu-3.0.0-1.fc29, kernel: 4.19.5-300.fc29.x86_64, hostname: localhost.localdomain LC_ALL=C \ PATH=/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin \ HOME=/home/berrange \ USER=berrange \ LOGNAME=berrange \ QEMU_AUDIO_DRV=none \ /usr/bin/qemu-system-ppc64 \ -name guest=guest,debug-threads=on \ -S \ -object secret,id=masterKey0,format=raw,file=/home/berrange/.config/libvirt/qemu/lib/domain-33-guest/master-key.aes \ -machine pseries-2.10,accel=tcg,usb=off,dump-guest-core=off \ -m 1024 \ -realtime mlock=off \ -smp 1,sockets=1,cores=1,threads=1 \ -uuid c8a74977-ab18-41d0-ae3b-4041c7fffbcd \ -display none \ -no-user-config \ -nodefaults \ -chardev socket,id=charmonitor,fd=23,server,nowait \ -mon chardev=charmonitor,id=monitor,mode=control \ -rtc base=utc \ -no-shutdown \ -boot strict=on \ -device qemu-xhci,id=usb,bus=pci.0,addr=0x1 \ -device virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x2 \ -sandbox on,obsolete=deny,elevateprivileges=deny,spawn=deny,resourcecontrol=deny \ -msg timestamp=on 2018-12-14 12:57:03.730+0000: shutting down, reason=failed Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
2018-12-14 12:07:08 +00:00
virBufferAdd(&buf, virCommandToString(loadcmd, false), -1);
virBufferAddChar(&buf, '\n');
}
if (!(cmd = virBhyveProcessBuildBhyveCmd(privconn, def, true)))
goto cleanup;
qemu: use line breaks in command line args written to log The QEMU command line arguments are very long and currently all written on a single line to /var/log/libvirt/qemu/$GUEST.log. This introduces logic to add line breaks after every env variable and "-" optional argument, and every positional argument. This will create a clearer log file, which will in turn present better in bug reports when people cut + paste from the log into a bug comment. An example log file entry now looks like this: 2018-12-14 12:57:03.677+0000: starting up libvirt version: 5.0.0, qemu version: 3.0.0qemu-3.0.0-1.fc29, kernel: 4.19.5-300.fc29.x86_64, hostname: localhost.localdomain LC_ALL=C \ PATH=/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin \ HOME=/home/berrange \ USER=berrange \ LOGNAME=berrange \ QEMU_AUDIO_DRV=none \ /usr/bin/qemu-system-ppc64 \ -name guest=guest,debug-threads=on \ -S \ -object secret,id=masterKey0,format=raw,file=/home/berrange/.config/libvirt/qemu/lib/domain-33-guest/master-key.aes \ -machine pseries-2.10,accel=tcg,usb=off,dump-guest-core=off \ -m 1024 \ -realtime mlock=off \ -smp 1,sockets=1,cores=1,threads=1 \ -uuid c8a74977-ab18-41d0-ae3b-4041c7fffbcd \ -display none \ -no-user-config \ -nodefaults \ -chardev socket,id=charmonitor,fd=23,server,nowait \ -mon chardev=charmonitor,id=monitor,mode=control \ -rtc base=utc \ -no-shutdown \ -boot strict=on \ -device qemu-xhci,id=usb,bus=pci.0,addr=0x1 \ -device virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x2 \ -sandbox on,obsolete=deny,elevateprivileges=deny,spawn=deny,resourcecontrol=deny \ -msg timestamp=on 2018-12-14 12:57:03.730+0000: shutting down, reason=failed Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
2018-12-14 12:07:08 +00:00
virBufferAdd(&buf, virCommandToString(cmd, false), -1);
ret = virBufferContentAndReset(&buf);
cleanup:
virCommandFree(loadcmd);
virCommandFree(cmd);
virDomainDefFree(def);
return ret;
}
static int
bhyveConnectListAllDomains(virConnectPtr conn,
virDomainPtr **domains,
unsigned int flags)
{
bhyveConnPtr privconn = conn->privateData;
virCheckFlags(VIR_CONNECT_LIST_DOMAINS_FILTERS_ALL, -1);
if (virConnectListAllDomainsEnsureACL(conn) < 0)
return -1;
return virDomainObjListExport(privconn->domains, conn, domains,
virConnectListAllDomainsCheckACL, flags);
}
static virDomainPtr
bhyveDomainLookupByUUID(virConnectPtr conn,
const unsigned char *uuid)
{
bhyveConnPtr privconn = conn->privateData;
virDomainObjPtr vm;
virDomainPtr dom = NULL;
vm = virDomainObjListFindByUUID(privconn->domains, uuid);
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 virDomainPtr bhyveDomainLookupByName(virConnectPtr conn,
const char *name)
{
bhyveConnPtr privconn = conn->privateData;
virDomainObjPtr vm;
virDomainPtr dom = NULL;
vm = virDomainObjListFindByName(privconn->domains, name);
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
bhyveDomainLookupByID(virConnectPtr conn,
int id)
{
bhyveConnPtr privconn = conn->privateData;
virDomainObjPtr vm;
virDomainPtr dom = NULL;
vm = virDomainObjListFindByID(privconn->domains, id);
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 int
bhyveDomainCreateWithFlags(virDomainPtr dom,
unsigned int flags)
{
bhyveConnPtr privconn = dom->conn->privateData;
virDomainObjPtr vm;
virObjectEventPtr event = NULL;
unsigned int start_flags = 0;
int ret = -1;
virCheckFlags(VIR_DOMAIN_START_AUTODESTROY, -1);
if (flags & VIR_DOMAIN_START_AUTODESTROY)
start_flags |= VIR_BHYVE_PROCESS_START_AUTODESTROY;
if (!(vm = bhyveDomObjFromDomain(dom)))
goto cleanup;
if (virDomainCreateWithFlagsEnsureACL(dom->conn, vm->def) < 0)
goto cleanup;
if (virDomainObjIsActive(vm)) {
virReportError(VIR_ERR_OPERATION_INVALID,
"%s", _("Domain is already running"));
goto cleanup;
}
ret = virBhyveProcessStart(dom->conn, privconn, vm,
VIR_DOMAIN_RUNNING_BOOTED,
start_flags);
if (ret == 0)
event = virDomainEventLifecycleNewFromObj(vm,
VIR_DOMAIN_EVENT_STARTED,
VIR_DOMAIN_EVENT_STARTED_BOOTED);
cleanup:
virDomainObjEndAPI(&vm);
virObjectEventStateQueue(privconn->domainEventState, event);
return ret;
}
static int
bhyveDomainCreate(virDomainPtr dom)
{
return bhyveDomainCreateWithFlags(dom, 0);
}
static virDomainPtr
bhyveDomainCreateXML(virConnectPtr conn,
const char *xml,
unsigned int flags)
{
bhyveConnPtr privconn = conn->privateData;
virDomainPtr dom = NULL;
virDomainDefPtr def = NULL;
virDomainObjPtr vm = NULL;
virObjectEventPtr event = NULL;
unsigned int start_flags = 0;
unsigned int parse_flags = VIR_DOMAIN_DEF_PARSE_INACTIVE;
virCheckFlags(VIR_DOMAIN_START_AUTODESTROY |
VIR_DOMAIN_START_VALIDATE, NULL);
if (flags & VIR_DOMAIN_START_VALIDATE)
parse_flags |= VIR_DOMAIN_DEF_PARSE_VALIDATE_SCHEMA;
if (flags & VIR_DOMAIN_START_AUTODESTROY)
start_flags |= VIR_BHYVE_PROCESS_START_AUTODESTROY;
if ((def = virDomainDefParseString(xml, privconn->xmlopt,
NULL, parse_flags)) == NULL)
goto cleanup;
if (virDomainCreateXMLEnsureACL(conn, def) < 0)
goto cleanup;
if (bhyveDomainAssignAddresses(def, NULL) < 0)
goto cleanup;
if (!(vm = virDomainObjListAdd(privconn->domains, def,
privconn->xmlopt,
VIR_DOMAIN_OBJ_LIST_ADD_LIVE |
VIR_DOMAIN_OBJ_LIST_ADD_CHECK_LIVE, NULL)))
goto cleanup;
def = NULL;
if (virBhyveProcessStart(conn, privconn, vm,
VIR_DOMAIN_RUNNING_BOOTED,
start_flags) < 0) {
/* If domain is not persistent, remove its data */
conf: Clean up object referencing for Add and Remove When adding a new object to the domain object list, there should have been 2 virObjectRef calls made one for each list into which the object was placed to match the 2 virObjectUnref calls that would occur during Remove as part of virHashRemoveEntry when virObjectFreeHashData is called when the element is removed from the hash table as set up in virDomainObjListNew. Some drivers (libxl, lxc, qemu, and vz) handled this inconsistency by calling virObjectRef upon successful return from virDomainObjListAdd in order to use virDomainObjEndAPI when done with the returned @vm. While others (bhyve, openvz, test, and vmware) handled this via only calling virObjectUnlock upon successful return from virDomainObjListAdd. This patch will "unify" the approach to use virDomainObjEndAPI for any @vm successfully returned from virDomainObjListAdd. Because list removal is so tightly coupled with list addition, this patch fixes the list removal algorithm to return the object as entered - "locked and reffed". This way, the callers can then decide how to uniformly handle add/remove success and failure. This removes the onus on the caller to "specially handle" the @vm during removal processing. The Add/Remove logic allows for some logic simplification such as in libxl where we can Remove the @vm directly rather than needing to set a @remove_dom boolean and removing after the libxlDomainObjEndJob completes as the @vm is locked/reffed. Signed-off-by: John Ferlan <jferlan@redhat.com> Reviewed-by: Erik Skultety <eskultet@redhat.com>
2018-04-23 14:40:48 +00:00
if (!vm->persistent)
virDomainObjListRemove(privconn->domains, vm);
goto cleanup;
}
event = virDomainEventLifecycleNewFromObj(vm,
VIR_DOMAIN_EVENT_STARTED,
VIR_DOMAIN_EVENT_STARTED_BOOTED);
dom = virGetDomain(conn, vm->def->name, vm->def->uuid, vm->def->id);
cleanup:
virDomainDefFree(def);
conf: Clean up object referencing for Add and Remove When adding a new object to the domain object list, there should have been 2 virObjectRef calls made one for each list into which the object was placed to match the 2 virObjectUnref calls that would occur during Remove as part of virHashRemoveEntry when virObjectFreeHashData is called when the element is removed from the hash table as set up in virDomainObjListNew. Some drivers (libxl, lxc, qemu, and vz) handled this inconsistency by calling virObjectRef upon successful return from virDomainObjListAdd in order to use virDomainObjEndAPI when done with the returned @vm. While others (bhyve, openvz, test, and vmware) handled this via only calling virObjectUnlock upon successful return from virDomainObjListAdd. This patch will "unify" the approach to use virDomainObjEndAPI for any @vm successfully returned from virDomainObjListAdd. Because list removal is so tightly coupled with list addition, this patch fixes the list removal algorithm to return the object as entered - "locked and reffed". This way, the callers can then decide how to uniformly handle add/remove success and failure. This removes the onus on the caller to "specially handle" the @vm during removal processing. The Add/Remove logic allows for some logic simplification such as in libxl where we can Remove the @vm directly rather than needing to set a @remove_dom boolean and removing after the libxlDomainObjEndJob completes as the @vm is locked/reffed. Signed-off-by: John Ferlan <jferlan@redhat.com> Reviewed-by: Erik Skultety <eskultet@redhat.com>
2018-04-23 14:40:48 +00:00
virDomainObjEndAPI(&vm);
virObjectEventStateQueue(privconn->domainEventState, event);
return dom;
}
static int
bhyveDomainDestroyFlags(virDomainPtr dom, unsigned int flags)
{
bhyveConnPtr privconn = dom->conn->privateData;
virDomainObjPtr vm;
virObjectEventPtr event = NULL;
int ret = -1;
virCheckFlags(0, -1);
if (!(vm = bhyveDomObjFromDomain(dom)))
goto cleanup;
if (virDomainDestroyFlagsEnsureACL(dom->conn, vm->def) < 0)
goto cleanup;
if (virDomainObjCheckActive(vm) < 0)
goto cleanup;
ret = virBhyveProcessStop(privconn, vm, VIR_DOMAIN_SHUTOFF_DESTROYED);
event = virDomainEventLifecycleNewFromObj(vm,
VIR_DOMAIN_EVENT_STOPPED,
VIR_DOMAIN_EVENT_STOPPED_DESTROYED);
conf: Clean up object referencing for Add and Remove When adding a new object to the domain object list, there should have been 2 virObjectRef calls made one for each list into which the object was placed to match the 2 virObjectUnref calls that would occur during Remove as part of virHashRemoveEntry when virObjectFreeHashData is called when the element is removed from the hash table as set up in virDomainObjListNew. Some drivers (libxl, lxc, qemu, and vz) handled this inconsistency by calling virObjectRef upon successful return from virDomainObjListAdd in order to use virDomainObjEndAPI when done with the returned @vm. While others (bhyve, openvz, test, and vmware) handled this via only calling virObjectUnlock upon successful return from virDomainObjListAdd. This patch will "unify" the approach to use virDomainObjEndAPI for any @vm successfully returned from virDomainObjListAdd. Because list removal is so tightly coupled with list addition, this patch fixes the list removal algorithm to return the object as entered - "locked and reffed". This way, the callers can then decide how to uniformly handle add/remove success and failure. This removes the onus on the caller to "specially handle" the @vm during removal processing. The Add/Remove logic allows for some logic simplification such as in libxl where we can Remove the @vm directly rather than needing to set a @remove_dom boolean and removing after the libxlDomainObjEndJob completes as the @vm is locked/reffed. Signed-off-by: John Ferlan <jferlan@redhat.com> Reviewed-by: Erik Skultety <eskultet@redhat.com>
2018-04-23 14:40:48 +00:00
if (!vm->persistent)
virDomainObjListRemove(privconn->domains, vm);
cleanup:
virDomainObjEndAPI(&vm);
virObjectEventStateQueue(privconn->domainEventState, event);
return ret;
}
static int
bhyveDomainDestroy(virDomainPtr dom)
{
return bhyveDomainDestroyFlags(dom, 0);
}
static int
bhyveDomainShutdownFlags(virDomainPtr dom, unsigned int flags)
{
virDomainObjPtr vm;
int ret = -1;
virCheckFlags(0, -1);
if (!(vm = bhyveDomObjFromDomain(dom)))
goto cleanup;
if (virDomainShutdownFlagsEnsureACL(dom->conn, vm->def, flags) < 0)
goto cleanup;
if (virDomainObjCheckActive(vm) < 0)
goto cleanup;
ret = virBhyveProcessShutdown(vm);
cleanup:
virDomainObjEndAPI(&vm);
return ret;
}
static int
bhyveDomainShutdown(virDomainPtr dom)
{
return bhyveDomainShutdownFlags(dom, 0);
}
static int
bhyveDomainOpenConsole(virDomainPtr dom,
const char *dev_name G_GNUC_UNUSED,
virStreamPtr st,
unsigned int flags)
{
virDomainObjPtr vm = NULL;
virDomainChrDefPtr chr = NULL;
int ret = -1;
virCheckFlags(0, -1);
if (!(vm = bhyveDomObjFromDomain(dom)))
goto cleanup;
if (virDomainOpenConsoleEnsureACL(dom->conn, vm->def) < 0)
goto cleanup;
if (virDomainObjCheckActive(vm) < 0)
goto cleanup;
if (!vm->def->nserials) {
virReportError(VIR_ERR_INTERNAL_ERROR,
"%s", _("no console devices available"));
goto cleanup;
}
chr = vm->def->serials[0];
if (virFDStreamOpenPTY(st, chr->source->data.nmdm.slave,
0, 0, O_RDWR) < 0)
goto cleanup;
ret = 0;
cleanup:
virDomainObjEndAPI(&vm);
return ret;
}
static int
bhyveDomainSetMetadata(virDomainPtr dom,
int type,
const char *metadata,
const char *key,
const char *uri,
unsigned int flags)
{
bhyveConnPtr privconn = dom->conn->privateData;
virDomainObjPtr vm;
int ret = -1;
virCheckFlags(VIR_DOMAIN_AFFECT_LIVE |
VIR_DOMAIN_AFFECT_CONFIG, -1);
if (!(vm = bhyveDomObjFromDomain(dom)))
return -1;
if (virDomainSetMetadataEnsureACL(dom->conn, vm->def, flags) < 0)
goto cleanup;
ret = virDomainObjSetMetadata(vm, type, metadata, key, uri,
privconn->xmlopt, BHYVE_STATE_DIR,
BHYVE_CONFIG_DIR, flags);
if (ret == 0) {
virObjectEventPtr ev = NULL;
ev = virDomainEventMetadataChangeNewFromObj(vm, type, uri);
virObjectEventStateQueue(privconn->domainEventState, ev);
}
cleanup:
virDomainObjEndAPI(&vm);
return ret;
}
static char *
bhyveDomainGetMetadata(virDomainPtr dom,
int type,
const char *uri,
unsigned int flags)
{
virDomainObjPtr vm;
char *ret = NULL;
if (!(vm = bhyveDomObjFromDomain(dom)))
return NULL;
if (virDomainGetMetadataEnsureACL(dom->conn, vm->def) < 0)
goto cleanup;
ret = virDomainObjGetMetadata(vm, type, uri, flags);
cleanup:
virDomainObjEndAPI(&vm);
return ret;
}
static int
bhyveNodeGetCPUStats(virConnectPtr conn,
int cpuNum,
virNodeCPUStatsPtr params,
int *nparams,
unsigned int flags)
{
if (virNodeGetCPUStatsEnsureACL(conn) < 0)
return -1;
return virHostCPUGetStats(cpuNum, params, nparams, flags);
}
static int
bhyveNodeGetMemoryStats(virConnectPtr conn,
int cellNum,
virNodeMemoryStatsPtr params,
int *nparams,
unsigned int flags)
{
if (virNodeGetMemoryStatsEnsureACL(conn) < 0)
return -1;
return virHostMemGetStats(cellNum, params, nparams, flags);
}
static int
bhyveNodeGetInfo(virConnectPtr conn,
virNodeInfoPtr nodeinfo)
{
if (virNodeGetInfoEnsureACL(conn) < 0)
return -1;
return virCapabilitiesGetNodeInfo(nodeinfo);
}
static int
bhyveStateCleanup(void)
{
VIR_DEBUG("bhyve state cleanup");
if (bhyve_driver == NULL)
return -1;
virObjectUnref(bhyve_driver->domains);
virObjectUnref(bhyve_driver->caps);
virObjectUnref(bhyve_driver->xmlopt);
2016-03-28 12:49:28 +00:00
virSysinfoDefFree(bhyve_driver->hostsysinfo);
virObjectUnref(bhyve_driver->closeCallbacks);
virObjectUnref(bhyve_driver->domainEventState);
virObjectUnref(bhyve_driver->config);
virPortAllocatorRangeFree(bhyve_driver->remotePorts);
if (bhyve_driver->lockFD != -1)
virPidFileRelease(BHYVE_STATE_DIR, "driver", bhyve_driver->lockFD);
virMutexDestroy(&bhyve_driver->lock);
VIR_FREE(bhyve_driver);
return 0;
}
static int
bhyveStateInitialize(bool privileged,
const char *root,
virStateInhibitCallback callback G_GNUC_UNUSED,
void *opaque G_GNUC_UNUSED)
{
bool autostart = true;
if (root != NULL) {
virReportError(VIR_ERR_INVALID_ARG, "%s",
_("Driver does not support embedded mode"));
return -1;
}
if (!privileged) {
VIR_INFO("Not running privileged, disabling driver");
return VIR_DRV_STATE_INIT_SKIPPED;
}
if (VIR_ALLOC(bhyve_driver) < 0)
return VIR_DRV_STATE_INIT_ERROR;
bhyve_driver->lockFD = -1;
if (virMutexInit(&bhyve_driver->lock) < 0) {
VIR_FREE(bhyve_driver);
return VIR_DRV_STATE_INIT_ERROR;
}
if (!(bhyve_driver->closeCallbacks = virCloseCallbacksNew()))
goto cleanup;
if (!(bhyve_driver->caps = virBhyveCapsBuild()))
goto cleanup;
if (virBhyveProbeCaps(&bhyve_driver->bhyvecaps) < 0)
goto cleanup;
if (virBhyveProbeGrubCaps(&bhyve_driver->grubcaps) < 0)
goto cleanup;
if (!(bhyve_driver->xmlopt = virBhyveDriverCreateXMLConf(bhyve_driver)))
goto cleanup;
if (!(bhyve_driver->domains = virDomainObjListNew()))
goto cleanup;
if (!(bhyve_driver->domainEventState = virObjectEventStateNew()))
goto cleanup;
if (!(bhyve_driver->remotePorts = virPortAllocatorRangeNew(_("display"),
5900, 65535)))
goto cleanup;
2014-04-23 09:24:05 +00:00
bhyve_driver->hostsysinfo = virSysinfoRead();
if (!(bhyve_driver->config = virBhyveDriverConfigNew()))
goto cleanup;
if (virBhyveLoadDriverConfig(bhyve_driver->config, SYSCONFDIR "/libvirt/bhyve.conf") < 0)
goto cleanup;
if (virFileMakePath(BHYVE_LOG_DIR) < 0) {
virReportSystemError(errno,
_("Failed to mkdir %s"),
BHYVE_LOG_DIR);
goto cleanup;
}
if (virFileMakePath(BHYVE_STATE_DIR) < 0) {
virReportSystemError(errno,
_("Failed to mkdir %s"),
BHYVE_STATE_DIR);
goto cleanup;
}
if ((bhyve_driver->lockFD =
virPidFileAcquire(BHYVE_STATE_DIR, "driver", false, getpid())) < 0)
goto cleanup;
if (virDomainObjListLoadAllConfigs(bhyve_driver->domains,
BHYVE_STATE_DIR,
NULL, true,
bhyve_driver->xmlopt,
NULL, NULL) < 0)
goto cleanup;
if (virDomainObjListLoadAllConfigs(bhyve_driver->domains,
BHYVE_CONFIG_DIR,
BHYVE_AUTOSTART_DIR, false,
bhyve_driver->xmlopt,
NULL, NULL) < 0)
goto cleanup;
virBhyveProcessReconnectAll(bhyve_driver);
if (virDriverShouldAutostart(BHYVE_STATE_DIR, &autostart) < 0)
goto cleanup;
if (autostart)
bhyveAutostartDomains(bhyve_driver);
return VIR_DRV_STATE_INIT_COMPLETE;
cleanup:
bhyveStateCleanup();
return VIR_DRV_STATE_INIT_ERROR;
}
unsigned
bhyveDriverGetBhyveCaps(bhyveConnPtr driver)
{
if (driver != NULL)
return driver->bhyvecaps;
return 0;
}
unsigned
bhyveDriverGetGrubCaps(bhyveConnPtr driver)
{
if (driver != NULL)
return driver->grubcaps;
return 0;
}
static int
bhyveConnectGetMaxVcpus(virConnectPtr conn,
const char *type)
{
if (virConnectGetMaxVcpusEnsureACL(conn) < 0)
return -1;
/*
* Bhyve supports up to 16 VCPUs, but offers no method to check this
* value. Hardcode 16...
*/
if (!type || STRCASEEQ(type, "bhyve"))
return 16;
virReportError(VIR_ERR_INVALID_ARG, _("unknown type '%s'"), type);
return -1;
}
static unsigned long long
bhyveNodeGetFreeMemory(virConnectPtr conn)
{
unsigned long long freeMem;
if (virNodeGetFreeMemoryEnsureACL(conn) < 0)
return 0;
if (virHostMemGetInfo(NULL, &freeMem) < 0)
return 0;
return freeMem;
}
static int
bhyveNodeGetCPUMap(virConnectPtr conn,
unsigned char **cpumap,
unsigned int *online,
unsigned int flags)
{
if (virNodeGetCPUMapEnsureACL(conn) < 0)
return -1;
return virHostCPUGetMap(cpumap, online, flags);
}
static int
bhyveNodeGetMemoryParameters(virConnectPtr conn,
virTypedParameterPtr params,
int *nparams,
unsigned int flags)
{
if (virNodeGetMemoryParametersEnsureACL(conn) < 0)
return -1;
return virHostMemGetParameters(params, nparams, flags);
}
static int
bhyveNodeSetMemoryParameters(virConnectPtr conn,
virTypedParameterPtr params,
int nparams,
unsigned int flags)
{
if (virNodeSetMemoryParametersEnsureACL(conn) < 0)
return -1;
return virHostMemSetParameters(params, nparams, flags);
}
static char *
bhyveConnectBaselineCPU(virConnectPtr conn,
const char **xmlCPUs,
unsigned int ncpus,
unsigned int flags)
{
virCPUDefPtr *cpus = NULL;
virCPUDefPtr cpu = NULL;
char *cpustr = NULL;
virCheckFlags(VIR_CONNECT_BASELINE_CPU_EXPAND_FEATURES |
VIR_CONNECT_BASELINE_CPU_MIGRATABLE, NULL);
if (virConnectBaselineCPUEnsureACL(conn) < 0)
goto cleanup;
if (!(cpus = virCPUDefListParse(xmlCPUs, ncpus, VIR_CPU_TYPE_HOST)))
goto cleanup;
if (!(cpu = virCPUBaseline(VIR_ARCH_NONE, cpus, ncpus, NULL, NULL,
!!(flags & VIR_CONNECT_BASELINE_CPU_MIGRATABLE))))
goto cleanup;
if ((flags & VIR_CONNECT_BASELINE_CPU_EXPAND_FEATURES) &&
virCPUExpandFeatures(cpus[0]->arch, cpu) < 0)
goto cleanup;
cpustr = virCPUDefFormat(cpu, NULL);
cleanup:
virCPUDefListFree(cpus);
virCPUDefFree(cpu);
return cpustr;
}
static int
bhyveConnectCompareCPU(virConnectPtr conn,
const char *xmlDesc,
unsigned int flags)
{
bhyveConnPtr driver = conn->privateData;
int ret = VIR_CPU_COMPARE_ERROR;
virCapsPtr caps = NULL;
bool failIncompatible;
virCheckFlags(VIR_CONNECT_COMPARE_CPU_FAIL_INCOMPATIBLE,
VIR_CPU_COMPARE_ERROR);
if (virConnectCompareCPUEnsureACL(conn) < 0)
goto cleanup;
failIncompatible = !!(flags & VIR_CONNECT_COMPARE_CPU_FAIL_INCOMPATIBLE);
if (!(caps = bhyveDriverGetCapabilities(driver)))
goto cleanup;
if (!caps->host.cpu ||
!caps->host.cpu->model) {
if (failIncompatible) {
virReportError(VIR_ERR_CPU_INCOMPATIBLE, "%s",
_("cannot get host CPU capabilities"));
} else {
VIR_WARN("cannot get host CPU capabilities");
ret = VIR_CPU_COMPARE_INCOMPATIBLE;
}
} else {
ret = virCPUCompareXML(caps->host.arch, caps->host.cpu,
xmlDesc, failIncompatible);
}
cleanup:
virObjectUnref(caps);
return ret;
}
static int
bhyveConnectDomainEventRegisterAny(virConnectPtr conn,
virDomainPtr dom,
int eventID,
virConnectDomainEventGenericCallback callback,
void *opaque,
virFreeCallback freecb)
{
bhyveConnPtr privconn = conn->privateData;
int ret;
if (virConnectDomainEventRegisterAnyEnsureACL(conn) < 0)
return -1;
if (virDomainEventStateRegisterID(conn,
privconn->domainEventState,
dom, eventID,
callback, opaque, freecb, &ret) < 0)
ret = -1;
return ret;
}
static int
bhyveConnectDomainEventDeregisterAny(virConnectPtr conn,
int callbackID)
{
bhyveConnPtr privconn = conn->privateData;
if (virConnectDomainEventDeregisterAnyEnsureACL(conn) < 0)
return -1;
if (virObjectEventStateDeregisterID(conn,
privconn->domainEventState,
events: Avoid double free possibility on remote call failure If a remote call fails during event registration (more than likely from a network failure or remote libvirtd restart timed just right), then when calling the virObjectEventStateDeregisterID we don't want to call the registered @freecb function because that breaks our contract that we would only call it after succesfully returning. If the @freecb routine were called, it could result in a double free from properly coded applications that free their opaque data on failure to register, as seen in the following details: Program terminated with signal 6, Aborted. #0 0x00007fc45cba15d7 in raise #1 0x00007fc45cba2cc8 in abort #2 0x00007fc45cbe12f7 in __libc_message #3 0x00007fc45cbe86d3 in _int_free #4 0x00007fc45d8d292c in PyDict_Fini #5 0x00007fc45d94f46a in Py_Finalize #6 0x00007fc45d960735 in Py_Main #7 0x00007fc45cb8daf5 in __libc_start_main #8 0x0000000000400721 in _start The double dereference of 'pyobj_cbData' is triggered in the following way: (1) libvirt_virConnectDomainEventRegisterAny is invoked. (2) the event is successfully added to the event callback list (virDomainEventStateRegisterClient in remoteConnectDomainEventRegisterAny returns 1 which means ok). (3) when function remoteConnectDomainEventRegisterAny is hit, network connection disconnected coincidently (or libvirtd is restarted) in the context of function 'call' then the connection is lost and the function 'call' failed, the branch virObjectEventStateDeregisterID is therefore taken. (4) 'pyobj_conn' is dereferenced the 1st time in libvirt_virConnectDomainEventFreeFunc. (5) 'pyobj_cbData' (refered to pyobj_conn) is dereferenced the 2nd time in libvirt_virConnectDomainEventRegisterAny. (6) the double free error is triggered. Resolve this by adding a @doFreeCb boolean in order to avoid calling the freeCb in virObjectEventStateDeregisterID for any remote call failure in a remoteConnect*EventRegister* API. For remoteConnect*EventDeregister* calls, the passed value would be true indicating they should run the freecb if it exists; whereas, it's false for the remote call failure path. Patch based on the investigation and initial patch posted by fangying <fangying1@huawei.com>.
2017-06-14 11:32:15 +00:00
callbackID, true) < 0)
return -1;
return 0;
}
static int
bhyveDomainHasManagedSaveImage(virDomainPtr domain, unsigned int flags)
{
virDomainObjPtr vm = NULL;
int ret = -1;
virCheckFlags(0, -1);
if (!(vm = bhyveDomObjFromDomain(domain)))
goto cleanup;
if (virDomainHasManagedSaveImageEnsureACL(domain->conn, vm->def) < 0)
goto cleanup;
ret = 0;
cleanup:
virDomainObjEndAPI(&vm);
return ret;
}
static const char *
bhyveConnectGetType(virConnectPtr conn)
{
if (virConnectGetTypeEnsureACL(conn) < 0)
return NULL;
return "BHYVE";
}
static int bhyveConnectIsAlive(virConnectPtr conn G_GNUC_UNUSED)
{
return 1;
}
static int
bhyveConnectIsSecure(virConnectPtr conn G_GNUC_UNUSED)
{
/* Trivially secure, since always inside the daemon */
return 1;
}
static int
bhyveConnectIsEncrypted(virConnectPtr conn G_GNUC_UNUSED)
{
/* Not encrypted, but remote driver takes care of that */
return 0;
}
static char *
bhyveConnectDomainXMLFromNative(virConnectPtr conn,
const char *nativeFormat,
const char *nativeConfig,
unsigned int flags)
{
char *xml = NULL;
virDomainDefPtr def = NULL;
bhyveConnPtr privconn = conn->privateData;
unsigned bhyveCaps = bhyveDriverGetBhyveCaps(privconn);
virCheckFlags(0, NULL);
if (virConnectDomainXMLFromNativeEnsureACL(conn) < 0)
return NULL;
if (STRNEQ(nativeFormat, BHYVE_CONFIG_FORMAT_ARGV)) {
virReportError(VIR_ERR_INVALID_ARG,
_("unsupported config type %s"), nativeFormat);
goto cleanup;
}
def = bhyveParseCommandLineString(nativeConfig, bhyveCaps,
privconn->xmlopt);
if (def == NULL)
goto cleanup;
xml = virDomainDefFormat(def, privconn->xmlopt, 0);
cleanup:
virDomainDefFree(def);
return xml;
}
static char *
bhyveConnectGetDomainCapabilities(virConnectPtr conn,
const char *emulatorbin,
const char *arch_str,
const char *machine,
const char *virttype_str,
unsigned int flags)
{
virDomainCapsPtr caps = NULL;
char *ret = NULL;
int virttype = VIR_DOMAIN_VIRT_BHYVE;
int arch = virArchFromHost(); /* virArch */
virCheckFlags(0, ret);
if (virConnectGetDomainCapabilitiesEnsureACL(conn) < 0)
return ret;
if (virttype_str &&
(virttype = virDomainVirtTypeFromString(virttype_str)) < 0) {
virReportError(VIR_ERR_INVALID_ARG,
_("unknown virttype: %s"),
virttype_str);
goto cleanup;
}
if (virttype != VIR_DOMAIN_VIRT_BHYVE) {
virReportError(VIR_ERR_INVALID_ARG,
_("unknown virttype: %s"),
virttype_str);
goto cleanup;
}
if (arch_str && (arch = virArchFromString(arch_str)) == VIR_ARCH_NONE) {
virReportError(VIR_ERR_INVALID_ARG,
_("unknown architecture: %s"),
arch_str);
goto cleanup;
}
if (!ARCH_IS_X86(arch)) {
virReportError(VIR_ERR_NO_SUPPORT,
_("unsupported architecture: %s"),
virArchToString(arch));
goto cleanup;
}
if (emulatorbin == NULL) {
emulatorbin = "/usr/sbin/bhyve";
} else if (STRNEQ(emulatorbin, "/usr/sbin/bhyve")) {
virReportError(VIR_ERR_INVALID_ARG,
_("unknown emulator binary: %s"),
emulatorbin);
goto cleanup;
}
if (!(caps = virBhyveDomainCapsBuild(conn->privateData, emulatorbin,
machine, arch, virttype)))
goto cleanup;
ret = virDomainCapsFormat(caps);
cleanup:
virObjectUnref(caps);
return ret;
}
static virHypervisorDriver bhyveHypervisorDriver = {
.name = "bhyve",
.connectURIProbe = bhyveConnectURIProbe,
.connectOpen = bhyveConnectOpen, /* 1.2.2 */
.connectClose = bhyveConnectClose, /* 1.2.2 */
.connectGetVersion = bhyveConnectGetVersion, /* 1.2.2 */
.connectGetHostname = bhyveConnectGetHostname, /* 1.2.2 */
2014-04-23 09:24:05 +00:00
.connectGetSysinfo = bhyveConnectGetSysinfo, /* 1.2.5 */
.domainGetInfo = bhyveDomainGetInfo, /* 1.2.2 */
.domainGetState = bhyveDomainGetState, /* 1.2.2 */
.connectGetCapabilities = bhyveConnectGetCapabilities, /* 1.2.2 */
.connectListDomains = bhyveConnectListDomains, /* 1.2.2 */
.connectNumOfDomains = bhyveConnectNumOfDomains, /* 1.2.2 */
.connectListAllDomains = bhyveConnectListAllDomains, /* 1.2.2 */
.connectListDefinedDomains = bhyveConnectListDefinedDomains, /* 1.2.2 */
.connectNumOfDefinedDomains = bhyveConnectNumOfDefinedDomains, /* 1.2.2 */
.connectDomainXMLToNative = bhyveConnectDomainXMLToNative, /* 1.2.5 */
.domainCreate = bhyveDomainCreate, /* 1.2.2 */
.domainCreateWithFlags = bhyveDomainCreateWithFlags, /* 1.2.3 */
.domainCreateXML = bhyveDomainCreateXML, /* 1.2.4 */
.domainDestroy = bhyveDomainDestroy, /* 1.2.2 */
.domainDestroyFlags = bhyveDomainDestroyFlags, /* 5.6.0 */
.domainShutdown = bhyveDomainShutdown, /* 1.3.3 */
.domainShutdownFlags = bhyveDomainShutdownFlags, /* 5.6.0 */
.domainLookupByUUID = bhyveDomainLookupByUUID, /* 1.2.2 */
.domainLookupByName = bhyveDomainLookupByName, /* 1.2.2 */
.domainLookupByID = bhyveDomainLookupByID, /* 1.2.3 */
.domainDefineXML = bhyveDomainDefineXML, /* 1.2.2 */
.domainDefineXMLFlags = bhyveDomainDefineXMLFlags, /* 1.2.12 */
.domainUndefine = bhyveDomainUndefine, /* 1.2.2 */
.domainUndefineFlags = bhyveDomainUndefineFlags, /* 5.6.0 */
2015-10-20 17:32:42 +00:00
.domainGetOSType = bhyveDomainGetOSType, /* 1.2.21 */
.domainGetXMLDesc = bhyveDomainGetXMLDesc, /* 1.2.2 */
.domainIsActive = bhyveDomainIsActive, /* 1.2.2 */
.domainIsPersistent = bhyveDomainIsPersistent, /* 1.2.2 */
.domainGetAutostart = bhyveDomainGetAutostart, /* 1.2.4 */
.domainSetAutostart = bhyveDomainSetAutostart, /* 1.2.4 */
.domainOpenConsole = bhyveDomainOpenConsole, /* 1.2.4 */
.domainSetMetadata = bhyveDomainSetMetadata, /* 1.2.4 */
.domainGetMetadata = bhyveDomainGetMetadata, /* 1.2.4 */
.nodeGetCPUStats = bhyveNodeGetCPUStats, /* 1.2.2 */
.nodeGetMemoryStats = bhyveNodeGetMemoryStats, /* 1.2.2 */
.nodeGetInfo = bhyveNodeGetInfo, /* 1.2.3 */
.connectGetMaxVcpus = bhyveConnectGetMaxVcpus, /* 1.2.3 */
.nodeGetFreeMemory = bhyveNodeGetFreeMemory, /* 1.2.3 */
.nodeGetCPUMap = bhyveNodeGetCPUMap, /* 1.2.3 */
.nodeGetMemoryParameters = bhyveNodeGetMemoryParameters, /* 1.2.3 */
.nodeSetMemoryParameters = bhyveNodeSetMemoryParameters, /* 1.2.3 */
.connectBaselineCPU = bhyveConnectBaselineCPU, /* 1.2.4 */
.connectCompareCPU = bhyveConnectCompareCPU, /* 1.2.4 */
.connectDomainEventRegisterAny = bhyveConnectDomainEventRegisterAny, /* 1.2.5 */
.connectDomainEventDeregisterAny = bhyveConnectDomainEventDeregisterAny, /* 1.2.5 */
.domainHasManagedSaveImage = bhyveDomainHasManagedSaveImage, /* 1.2.13 */
.connectGetType = bhyveConnectGetType, /* 1.3.5 */
.connectIsAlive = bhyveConnectIsAlive, /* 1.3.5 */
.connectIsSecure = bhyveConnectIsSecure, /* 1.3.5 */
.connectIsEncrypted = bhyveConnectIsEncrypted, /* 1.3.5 */
.connectDomainXMLFromNative = bhyveConnectDomainXMLFromNative, /* 2.1.0 */
.connectGetDomainCapabilities = bhyveConnectGetDomainCapabilities, /* 2.1.0 */
};
static virConnectDriver bhyveConnectDriver = {
.localOnly = true,
.uriSchemes = (const char *[]){ "bhyve", NULL },
.hypervisorDriver = &bhyveHypervisorDriver,
};
static virStateDriver bhyveStateDriver = {
.name = "bhyve",
.stateInitialize = bhyveStateInitialize,
.stateCleanup = bhyveStateCleanup,
};
int
bhyveRegister(void)
{
if (virRegisterConnectDriver(&bhyveConnectDriver,
true) < 0)
return -1;
if (virRegisterStateDriver(&bhyveStateDriver) < 0)
return -1;
return 0;
}