At this point it has a limited functionality and is highly
experimental. Supported domain operations are:
* define
* start
* destroy
It's only possible to have only one disk device and only one
network, which should be of type bridge.
---
configure.ac | 37 ++++
daemon/libvirtd.c | 9 +
include/libvirt/virterror.h | 1 +
po/POTFILES.in | 2 +
src/Makefile.am | 35 +++
src/bhyve/bhyve_driver.c | 514 ++++++++++++++++++++++++++++++++++++++++++++
src/bhyve/bhyve_driver.h | 28 +++
src/bhyve/bhyve_process.c | 393 +++++++++++++++++++++++++++++++++
src/bhyve/bhyve_process.h | 36 ++++
src/bhyve/bhyve_utils.h | 48 +++++
src/conf/domain_conf.c | 3 +-
src/conf/domain_conf.h | 1 +
src/driver.h | 1 +
src/libvirt.c | 3 +
src/util/virerror.c | 1 +
15 files changed, 1111 insertions(+), 1 deletion(-)
create mode 100644 src/bhyve/bhyve_driver.c
create mode 100644 src/bhyve/bhyve_driver.h
create mode 100644 src/bhyve/bhyve_process.c
create mode 100644 src/bhyve/bhyve_process.h
create mode 100644 src/bhyve/bhyve_utils.h
diff --git a/configure.ac b/configure.ac
index 2622dfd..edfe7d4 100644
--- a/configure.ac
+++ b/configure.ac
@@ -504,6 +504,10 @@ AC_ARG_WITH([parallels],
[AS_HELP_STRING([--with-parallels],
[add Parallels Cloud Server support @<:@default=check@:>@])])
m4_divert_text([DEFAULTS], [with_parallels=check])
+AC_ARG_WITH([bhyve],
+ [AS_HELP_STRING([--with-bhyve],
+ [add BHyVe support @<:@default=check@:>@])])
+m4_divert_text([DEFAULTS], [with_bhyve=check])
AC_ARG_WITH([test],
[AS_HELP_STRING([--with-test],
[add test driver support @<:@default=yes@:>@])])
@@ -1011,6 +1015,38 @@ fi
AM_CONDITIONAL([WITH_PARALLELS], [test "$with_parallels" = "yes"])
dnl
+dnl Checks for bhyve driver
+dnl
+
+if test "$with_bhyve" != "no"; then
+ AC_PATH_PROG([BHYVE], [bhyve], [], [$PATH:/usr/sbin])
+ AC_PATH_PROG([BHYVECTL], [bhyvectl], [$PATH:/usr/sbin])
+ AC_PATH_PROG([BHYVELOAD], [bhyveload], [$PATH:/usr/sbin/])
+
+ if test -z "$BHYVE" || test -z "$BHYVECTL" \
+ test -z "$BHYVELOAD" || test "$with_freebsd" =
"no"; then
+ if test "$with_bhyve" = "check"; then
+ with_bhyve="no"
+ else
+ AC_MSG_ERROR([The bhyve driver cannot be enabled])
+ fi
+ else
+ with_bhyve="yes"
+ fi
+fi
+
+if test "$with_bhyve" = "yes"; then
+ AC_DEFINE_UNQUOTED([WITH_BHYVE], 1, [whether bhyve driver is enabled])
+ AC_DEFINE_UNQUOTED([BHYVE], ["$BHYVE"],
+ [Location of the bhyve tool])
+ AC_DEFINE_UNQUOTED([BHYVECTL], ["$BHYVECTL"],
+ [Location of the bhyvectl tool])
+ AC_DEFINE_UNQUOTED([BHYVELOAD], ["$BHYVELOAD"],
+ [Location of the bhyveload tool])
+fi
+AM_CONDITIONAL([WITH_BHYVE], [test "$with_bhyve" = "yes"])
+
+dnl
dnl check for shell that understands <> redirection without truncation,
dnl needed by src/qemu/qemu_monitor_{text,json}.c.
dnl
@@ -2582,6 +2618,7 @@ AC_MSG_NOTICE([ PHYP: $with_phyp])
AC_MSG_NOTICE([ ESX: $with_esx])
AC_MSG_NOTICE([ Hyper-V: $with_hyperv])
AC_MSG_NOTICE([Parallels: $with_parallels])
+AC_MSG_NOTICE([ Bhyve: $with_bhyve])
AC_MSG_NOTICE([ Test: $with_test])
AC_MSG_NOTICE([ Remote: $with_remote])
AC_MSG_NOTICE([ Network: $with_network])
diff --git a/daemon/libvirtd.c b/daemon/libvirtd.c
index 49c42ad..b27c6fd 100644
--- a/daemon/libvirtd.c
+++ b/daemon/libvirtd.c
@@ -77,6 +77,9 @@
# ifdef WITH_VBOX
# include "vbox/vbox_driver.h"
# endif
+# ifdef WITH_BHYVE
+# include "bhyve/bhyve_driver.h"
+# endif
# ifdef WITH_NETWORK
# include "network/bridge_driver.h"
# endif
@@ -405,6 +408,9 @@ static void daemonInitialize(void)
# ifdef WITH_VBOX
virDriverLoadModule("vbox");
# endif
+# ifdef WITH_BHYVE
+ virDriverLoadModule("bhyve");
+# endif
#else
# ifdef WITH_NETWORK
networkRegister();
@@ -442,6 +448,9 @@ static void daemonInitialize(void)
# ifdef WITH_VBOX
vboxRegister();
# endif
+# ifdef WITH_BHYVE
+ bhyveRegister();
+# endif
#endif
}
diff --git a/include/libvirt/virterror.h b/include/libvirt/virterror.h
index e31e9c4..7915bbb 100644
--- a/include/libvirt/virterror.h
+++ b/include/libvirt/virterror.h
@@ -121,6 +121,7 @@ typedef enum {
VIR_FROM_ACCESS = 55, /* Error from access control manager */
VIR_FROM_SYSTEMD = 56, /* Error from systemd code */
+ VIR_FROM_BHYVE = 57, /* Error from bhyve driver */
# ifdef VIR_ENUM_SENTINELS
VIR_ERR_DOMAIN_LAST
# endif
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 49dfc9c..b4f463b 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -8,6 +8,8 @@ gnulib/lib/gai_strerror.c
gnulib/lib/regcomp.c
src/access/viraccessdriverpolkit.c
src/access/viraccessmanager.c
+src/bhyve/bhyve_driver.c
+src/bhyve/bhyve_process.c
src/conf/capabilities.c
src/conf/cpu_conf.c
src/conf/device_conf.c
diff --git a/src/Makefile.am b/src/Makefile.am
index 57e163f..718bef4 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -772,6 +772,13 @@ PARALLELS_DRIVER_SOURCES = \
parallels/parallels_storage.c \
parallels/parallels_network.c
+BHYVE_DRIVER_SOURCES = \
+ bhyve/bhyve_driver.h \
+ bhyve/bhyve_driver.c \
+ bhyve/bhyve_process.c \
+ bhyve/bhyve_process.h \
+ bhyve/bhyve_utils.h
+
NETWORK_DRIVER_SOURCES = \
network/bridge_driver.h network/bridge_driver.c \
network/bridge_driver_platform.h \
@@ -1308,6 +1315,33 @@ libvirt_driver_parallels_la_CFLAGS = \
libvirt_driver_parallels_la_SOURCES = $(PARALLELS_DRIVER_SOURCES)
endif WITH_PARALLELS
+if WITH_BHYVE
+noinst_LTLIBRARIES += libvirt_driver_bhyve_impl.la
+libvirt_driver_bhyve_la_SOURCES =
+libvirt_driver_bhyve_la_LIBADD = libvirt_driver_bhyve_impl.la
+if WITH_DRIVER_MODULES
+mod_LTLIBRARIES += libvirt_driver_bhyve.la
+libvirt_driver_bhyve_la_LIBADD += ../gnulib/lib/libgnu.la
+libvirt_driver_bhyve_la_LDFLAGS = -module -avoid-version $(AM_LDFLAGS)
+else ! WITH_DRIVER_MODULES
+noinst_LTLIBRARIES += libvirt_driver_bhyve.la
+endif ! WITH_DRIVER_MODULES
+
+libvirt_driver_bhyve_impl_la_CFLAGS = \
+ -I$(top_srcdir)/src/conf \
+ $(AM_CFLAGS)
+libvirt_driver_bhyve_impl_la_LDFLAGS = $(AM_LDFLAGS)
+
+
+
+#noinst_LTLIBRARIES += libvirt_driver_bhyve.la
+#libvirt_la_BUILT_LIBADD += libvirt_driver_bhyve.la
+#libvirt_driver_bhyve_la_CFLAGS = \
+# -I$(top_srcdir)/src/conf $(AM_CFLAGS)
+libvirt_driver_bhyve_impl_la_SOURCES = $(BHYVE_DRIVER_SOURCES)
+
+endif WITH_BHYVE
+
if WITH_NETWORK
noinst_LTLIBRARIES += libvirt_driver_network_impl.la
libvirt_driver_network_la_SOURCES =
@@ -1642,6 +1676,7 @@ EXTRA_DIST += \
$(HYPERV_DRIVER_SOURCES) \
$(HYPERV_DRIVER_EXTRA_DIST) \
$(PARALLELS_DRIVER_SOURCES) \
+ $(BHYVE_DRIVER_SOURCES) \
$(NETWORK_DRIVER_SOURCES) \
$(INTERFACE_DRIVER_SOURCES) \
$(STORAGE_DRIVER_SOURCES) \
diff --git a/src/bhyve/bhyve_driver.c b/src/bhyve/bhyve_driver.c
new file mode 100644
index 0000000..ba62d1a
--- /dev/null
+++ b/src/bhyve/bhyve_driver.c
@@ -0,0 +1,514 @@
+/*
+ * bhyve_driver.c: core driver methods for managing bhyve guests
+ *
+ * Copyright (C) 2013 Roman Bogorodskiy
+ *
+ * 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/>.
+ *
+ * Author: Roman Bogorodskiy
+ */
+
+#include <config.h>
+
+#include "virerror.h"
+#include "datatypes.h"
+#include "virbuffer.h"
+#include "viruuid.h"
+#include "capabilities.h"
+#include "configmake.h"
+#include "viralloc.h"
+#include "network_conf.h"
+#include "interface_conf.h"
+#include "domain_audit.h"
+#include "domain_conf.h"
+#include "snapshot_conf.h"
+#include "fdstream.h"
+#include "storage_conf.h"
+#include "node_device_conf.h"
+#include "virxml.h"
+#include "virthread.h"
+#include "virlog.h"
+#include "virfile.h"
+#include "virtypedparam.h"
+#include "virrandom.h"
+#include "virstring.h"
+#include "cpu/cpu.h"
+
+#include "bhyve_driver.h"
+#include "bhyve_process.h"
+#include "bhyve_utils.h"
+
+#define VIR_FROM_THIS VIR_FROM_BHYVE
+
+bhyveConnPtr bhyve_driver = NULL;
+
+void
+bhyveDriverLock(bhyveConnPtr driver)
+{
+ virMutexLock(&driver->lock);
+}
+
+void
+bhyveDriverUnlock(bhyveConnPtr driver)
+{
+ virMutexUnlock(&driver->lock);
+}
+
+static virCapsPtr
+bhyveBuildCapabilities(void)
+{
+ virCapsPtr caps;
+ virCapsGuestPtr guest;
+
+ if ((caps = virCapabilitiesNew(virArchFromHost(),
+ 0, 0)) == NULL)
+ return NULL;
+
+ if ((guest = virCapabilitiesAddGuest(caps, "hvm",
+ VIR_ARCH_X86_64,
+ "bhyve",
+ NULL, 0, NULL)) == NULL)
+ goto error;
+
+ if (virCapabilitiesAddGuestDomain(guest,
+ "bhyve", NULL, NULL, 0, NULL) == NULL)
+ goto error;
+
+ return caps;
+
+error:
+ virObjectUnref(caps);
+ return NULL;
+}
+
+static char *
+bhyveConnectGetCapabilities(virConnectPtr conn)
+{
+ bhyveConnPtr privconn = conn->privateData;
+ char *xml;
+
+ bhyveDriverLock(privconn);
+ if ((xml = virCapabilitiesFormatXML(privconn->caps)) == NULL)
+ virReportOOMError();
+ bhyveDriverUnlock(privconn);
+
+ 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 virDrvOpenStatus
+bhyveConnectOpen(virConnectPtr conn,
+ virConnectAuthPtr auth ATTRIBUTE_UNUSED,
+ unsigned int flags)
+{
+ virCheckFlags(VIR_CONNECT_RO, VIR_DRV_OPEN_ERROR);
+
+ if (!conn->uri)
+ return VIR_DRV_OPEN_DECLINED;
+
+ if (!conn->uri->scheme || STRNEQ(conn->uri->scheme, "bhyve"))
+ return VIR_DRV_OPEN_DECLINED;
+
+ if (conn->uri->server)
+ return VIR_DRV_OPEN_DECLINED;
+
+ if (!STREQ_NULLABLE(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;
+ }
+
+ conn->privateData = bhyve_driver;
+
+ return VIR_DRV_OPEN_SUCCESS;
+}
+
+static int
+bhyveConnectClose(virConnectPtr conn)
+{
+ bhyveConnPtr privconn = conn->privateData;
+
+ bhyveDriverLock(privconn);
+ virObjectUnref(privconn->caps);
+ virObjectUnref(privconn->xmlopt);
+ virObjectUnref(privconn->domains);
+ conn->privateData = NULL;
+
+ bhyveDriverUnlock(privconn);
+ virMutexDestroy(&privconn->lock);
+
+ VIR_FREE(privconn);
+ return 0;
+}
+
+static char *
+bhyveConnectGetHostname(virConnectPtr conn ATTRIBUTE_UNUSED)
+{
+ return virGetHostname();
+}
+
+static int
+bhyveConnectGetVersion(virConnectPtr conn ATTRIBUTE_UNUSED, unsigned long *hvVer)
+{
+ if (virParseVersionString("0.0.1", hvVer, true) < 0) {
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+bhyveDomainGetInfo(virDomainPtr domain ATTRIBUTE_UNUSED, virDomainInfoPtr info
ATTRIBUTE_UNUSED)
+{
+ return 0;
+}
+
+static virDomainPtr
+bhyveDomainDefineXML(virConnectPtr conn, const char *xml)
+{
+ bhyveConnPtr privconn = conn->privateData;
+ virDomainPtr dom = NULL;
+ virDomainDefPtr def = NULL;
+ virDomainDefPtr oldDef = NULL;
+ virDomainObjPtr vm = NULL;
+
+ bhyveDriverLock(privconn);
+
+ if ((def = virDomainDefParseString(xml, privconn->caps, privconn->xmlopt,
+ 1 << VIR_DOMAIN_VIRT_BHYVE,
+ VIR_DOMAIN_XML_INACTIVE)) == NULL) {
+ virReportError(VIR_ERR_INVALID_ARG, "%s",
+ _("Can't parse XML desc"));
+ goto cleanup;
+ }
+
+ if (!(vm = virDomainObjListAdd(privconn->domains, def,
+ privconn->xmlopt,
+ 0, &oldDef)))
+ goto cleanup;
+
+ VIR_INFO("Creating domain '%s'", vm->def->name);
+ dom = virGetDomain(conn, vm->def->name, vm->def->uuid);
+ if (dom) dom->id = vm->def->id;
+
+ if (virDomainSaveConfig(BHYVE_CONFIG_DIR, vm->def) < 0) {
+ goto cleanup;
+ }
+
+cleanup:
+ virDomainDefFree(def);
+ if (vm)
+ virObjectUnlock(vm);
+
+ bhyveDriverUnlock(privconn);
+ return dom;
+}
+
+static int
+bhyveConnectListDomains(virConnectPtr conn, int *ids, int maxids)
+{
+ bhyveConnPtr privconn = conn->privateData;
+ int n;
+
+ n = virDomainObjListGetActiveIDs(privconn->domains, ids, maxids,
+ NULL, NULL);
+
+ return n;
+}
+
+static int
+bhyveConnectNumOfDomains(virConnectPtr conn)
+{
+ bhyveConnPtr privconn = conn->privateData;
+ int count;
+
+ count = virDomainObjListNumOfDomains(privconn->domains, true,
+ NULL, NULL);
+
+ return count;
+}
+
+static int
+bhyveConnectListDefinedDomains(virConnectPtr conn, char **const names,
+ int maxnames)
+{
+ bhyveConnPtr privconn = conn->privateData;
+ int n;
+
+ memset(names, 0, sizeof(*names) * maxnames);
+ n = virDomainObjListGetInactiveNames(privconn->domains, names,
+ maxnames, NULL, NULL);
+
+ return n;
+}
+
+static int
+bhyveConnectNumOfDefinedDomains(virConnectPtr conn)
+{
+ bhyveConnPtr privconn = conn->privateData;
+ int count;
+
+ count = virDomainObjListNumOfDomains(privconn->domains, false,
+ NULL, NULL);
+
+ return count;
+}
+
+static int
+bhyveConnectListAllDomains(virConnectPtr conn,
+ virDomainPtr **domains,
+ unsigned int flags)
+{
+ bhyveConnPtr privconn = conn->privateData;
+ int ret = -1;
+
+ virCheckFlags(VIR_CONNECT_LIST_DOMAINS_FILTERS_ALL, -1);
+
+ ret = virDomainObjListExport(privconn->domains, conn, domains,
+ NULL, flags);
+
+ return ret;
+}
+
+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;
+ }
+
+ dom = virGetDomain(conn, vm->def->name, vm->def->uuid);
+ if (dom)
+ dom->id = vm->def->id;
+
+cleanup:
+ if (vm)
+ virObjectUnlock(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;
+ }
+ dom = virGetDomain(conn, vm->def->name, vm->def->uuid);
+ if (dom)
+ dom->id = vm->def->id;
+
+cleanup:
+ if (vm)
+ virObjectUnlock(vm);
+ return dom;
+}
+
+static int
+bhyveDomainCreate(virDomainPtr dom)
+{
+ bhyveConnPtr privconn = dom->conn->privateData;
+ virDomainObjPtr vm;
+ int ret = -1;
+
+ if (!(vm = bhyveDomObjFromDomain(dom)))
+ 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);
+
+cleanup:
+ if (vm)
+ virObjectUnlock(vm);
+ return ret;
+}
+
+static int
+bhyveDomainDestroy(virDomainPtr dom)
+{
+ bhyveConnPtr privconn = dom->conn->privateData;
+ virDomainObjPtr vm;
+ int ret = -1;
+
+ if (!(vm = bhyveDomObjFromDomain(dom)))
+ goto cleanup;
+
+ ret = virBhyveProcessStop(privconn, vm, VIR_DOMAIN_SHUTOFF_DESTROYED);
+
+cleanup:
+ if (vm)
+ virObjectUnlock(vm);
+ return ret;
+}
+
+static int
+bhyveStateCleanup(void)
+{
+ VIR_INFO("bhyve state cleanup");
+
+ if (bhyve_driver == NULL)
+ return -1;
+
+ virObjectUnref(bhyve_driver->domains);
+
+ virMutexDestroy(&bhyve_driver->lock);
+ VIR_FREE(bhyve_driver);
+
+ return 0;
+}
+
+static int
+bhyveStateInitialize(bool priveleged ATTRIBUTE_UNUSED,
+ virStateInhibitCallback callback ATTRIBUTE_UNUSED,
+ void *opaque ATTRIBUTE_UNUSED)
+{
+ if (!priveleged) {
+ VIR_INFO("Not running priveleged, disabling driver");
+ return 0;
+ }
+
+ if (VIR_ALLOC(bhyve_driver) < 0) {
+ return -1;
+ }
+
+ if (virMutexInit(&bhyve_driver->lock) < 0) {
+ VIR_FREE(bhyve_driver);
+ return -1;
+ }
+
+ if (!(bhyve_driver->caps = bhyveBuildCapabilities()))
+ goto cleanup;
+
+ if (!(bhyve_driver->xmlopt = virDomainXMLOptionNew(NULL, NULL, NULL)))
+ goto cleanup;
+
+ if (!(bhyve_driver->domains = virDomainObjListNew()))
+ 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_LOG_DIR);
+ goto cleanup;
+ }
+
+ if (virDomainObjListLoadAllConfigs(bhyve_driver->domains,
+ BHYVE_CONFIG_DIR,
+ NULL, 0,
+ bhyve_driver->caps,
+ bhyve_driver->xmlopt,
+ 1 << VIR_DOMAIN_VIRT_BHYVE,
+ NULL, NULL) < 0)
+ goto cleanup;
+
+ return 0;
+
+cleanup:
+ bhyveStateCleanup();
+ return -1;
+}
+
+
+static virDriver bhyveDriver = {
+ .no = VIR_DRV_BHYVE,
+ .name = "bhyve",
+ .connectOpen = bhyveConnectOpen,
+ .connectClose = bhyveConnectClose,
+ .connectGetVersion = bhyveConnectGetVersion,
+ .connectGetHostname = bhyveConnectGetHostname,
+ .domainGetInfo = bhyveDomainGetInfo,
+ .connectGetCapabilities = bhyveConnectGetCapabilities,
+ .connectListDomains = bhyveConnectListDomains,
+ .connectNumOfDomains = bhyveConnectNumOfDomains,
+ .connectListAllDomains = bhyveConnectListAllDomains,
+ .connectListDefinedDomains = bhyveConnectListDefinedDomains,
+ .connectNumOfDefinedDomains = bhyveConnectNumOfDefinedDomains,
+ .domainCreate = bhyveDomainCreate,
+ .domainDestroy = bhyveDomainDestroy,
+ .domainLookupByUUID = bhyveDomainLookupByUUID,
+ .domainLookupByName = bhyveDomainLookupByName,
+ .domainDefineXML = bhyveDomainDefineXML,
+};
+
+
+static virStateDriver bhyveStateDriver = {
+ .name= "bhyve",
+ .stateInitialize = bhyveStateInitialize,
+ .stateCleanup = bhyveStateCleanup,
+};
+
+int
+bhyveRegister(void)
+{
+ virRegisterDriver(&bhyveDriver);
+ virRegisterStateDriver(&bhyveStateDriver);
+ return 0;
+}
diff --git a/src/bhyve/bhyve_driver.h b/src/bhyve/bhyve_driver.h
new file mode 100644
index 0000000..ffe91e5
--- /dev/null
+++ b/src/bhyve/bhyve_driver.h
@@ -0,0 +1,28 @@
+/*
+ * bhyve_driver.h: core driver methods for managing bhyve guests
+ *
+ * Copyright (C) 2013 Roman Bogorodskiy
+ *
+ * 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/>.
+ *
+ * Author: Roman Bogorodskiy <bogorodskiy(a)gmail.com>
+ */
+
+#ifndef __BHYVE_DRIVER_H__
+# define __BHYVE_DRIVER_H__
+
+int bhyveRegister(void);
+
+#endif /* __BHYVE_DRIVER_H__ */
diff --git a/src/bhyve/bhyve_process.c b/src/bhyve/bhyve_process.c
new file mode 100644
index 0000000..d481d58
--- /dev/null
+++ b/src/bhyve/bhyve_process.c
@@ -0,0 +1,393 @@
+/*
+ * bhyve_process.c: bhyve process management
+ *
+ * Copyright (C) 2013 Roman Bogorodskiy
+ *
+ * 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/types.h>
+#include <dirent.h>
+#include <sys/ioctl.h>
+#include <net/if.h>
+#include <net/if_tap.h>
+
+#include "bhyve_process.h"
+#include "datatypes.h"
+#include "virerror.h"
+#include "virlog.h"
+#include "virfile.h"
+#include "viralloc.h"
+#include "vircommand.h"
+#include "virstring.h"
+#include "virpidfile.h"
+#include "virprocess.h"
+#include "virnetdev.h"
+#include "virnetdevbridge.h"
+#include "virnetdevtap.h"
+
+#define VIR_FROM_THIS VIR_FROM_BHYVE
+
+static char*
+virTapGetRealDeviceName(char *name)
+{
+ /* This is an ugly hack, because if we rename
+ * tap device to vnet%d, its device name will be
+ * still /dev/tap%d, and bhyve tries too open /dev/tap%d,
+ * so we have to find the real name
+ */
+ char *ret = NULL;
+ struct dirent *dp;
+ char *devpath = NULL;
+ int fd;
+
+ DIR* dirp = opendir("/dev");
+ if (dirp == NULL) {
+ return NULL;
+ }
+
+ while ((dp = readdir(dirp)) != NULL) {
+ if (STRPREFIX(dp->d_name, "tap")) {
+ struct ifreq ifr;
+ if (virAsprintf(&devpath, "/dev/%s", dp->d_name) < 0) {
+ goto cleanup;
+ }
+ if ((fd = open(devpath, O_RDWR)) < 0)
+ goto cleanup;
+
+ if (ioctl(fd, TAPGIFNAME, (void *)&ifr) < 0)
+ goto cleanup;
+
+ if (STREQ(name, ifr.ifr_name)) {
+ /* we can ignore the return value
+ * because we still have nothing
+ * to do but return;
+ */
+ ignore_value(VIR_STRDUP(ret, dp->d_name));
+ goto cleanup;
+ }
+
+ VIR_FREE(devpath);
+ VIR_FORCE_CLOSE(fd);
+ }
+ }
+
+cleanup:
+ VIR_FREE(devpath);
+ VIR_FORCE_CLOSE(fd);
+ closedir(dirp);
+ return ret;
+}
+
+static virCommandPtr
+virBhyveProcessBuildDestroyCmd(bhyveConnPtr driver ATTRIBUTE_UNUSED,
+ virDomainObjPtr vm)
+{
+ virCommandPtr cmd = virCommandNew(BHYVECTL);
+
+ virCommandAddArg(cmd, "--destroy");
+ virCommandAddArgPair(cmd, "--vm", vm->def->name);
+
+ return cmd;
+}
+
+static virCommandPtr
+virBhyveProcessBuildLoadCmd(bhyveConnPtr driver ATTRIBUTE_UNUSED,
+ virDomainObjPtr vm)
+{
+ virCommandPtr cmd;
+ virDomainDiskDefPtr disk = vm->def->disks[0];
+
+ cmd = virCommandNew(BHYVELOAD);
+
+ /* Memory */
+ virCommandAddArg(cmd, "-m");
+ virCommandAddArgFormat(cmd, "%llu",
+ VIR_DIV_UP(vm->def->mem.max_balloon, 1024));
+
+ /* Image path */
+ virCommandAddArg(cmd, "-d");
+ virCommandAddArgFormat(cmd, disk->src);
+
+ /* VM name */
+ virCommandAddArg(cmd, vm->def->name);
+
+ return cmd;
+}
+
+static virCommandPtr
+virBhyveProcessBuildBhyveCmd(bhyveConnPtr driver ATTRIBUTE_UNUSED,
+ virDomainObjPtr vm)
+{
+ /*
+ * /usr/sbin/bhyve -c 2 -m 256 -AI -H -P \
+ * -s 0:0,hostbridge \
+ * -s 1:0,virtio-net,tap0 \
+ * -s 2:0,ahci-hd,${IMG} \
+ * -S 31,uart,stdio \
+ * vm0
+ */
+ virCommandPtr cmd = NULL;
+ virDomainDiskDefPtr disk = vm->def->disks[0];
+ virDomainNetDefPtr net = NULL;
+ char *brname = NULL;
+ char *realifname = NULL;
+ int *tapfd = NULL;
+
+ if (vm->def->nnets > 1) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+ _("Domain should have one and only one disk defined"));
+ return NULL;
+ } else if (vm->def->nnets == 1)
+ net = vm->def->nets[0];
+
+ if (net != NULL) {
+ int actualType = virDomainNetGetActualType(net);
+
+ if (actualType == VIR_DOMAIN_NET_TYPE_BRIDGE) {
+ if (VIR_STRDUP(brname, virDomainNetGetActualBridgeName(net)) < 0)
+ return NULL;
+ } else {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("Network type %d is not supported"),
+ virDomainNetGetActualType(net));
+ return NULL;
+ }
+
+ if (!net->ifname ||
+ STRPREFIX(net->ifname, VIR_NET_GENERATED_PREFIX) ||
+ strchr(net->ifname, '%')) {
+ VIR_FREE(net->ifname);
+ if (VIR_STRDUP(net->ifname, VIR_NET_GENERATED_PREFIX "%d") <
0) {
+ VIR_FREE(brname);
+ return NULL;
+ }
+ }
+
+ if (virNetDevTapCreateInBridgePort(brname, &net->ifname,
&net->mac,
+ vm->def->uuid, tapfd, 1,
+ virDomainNetGetActualVirtPortProfile(net),
+ virDomainNetGetActualVlan(net),
+ VIR_NETDEV_TAP_CREATE_IFUP |
VIR_NETDEV_TAP_CREATE_PERSIST) < 0) {
+ VIR_FREE(net->ifname);
+ VIR_FREE(brname);
+ return NULL;
+ }
+ }
+
+ realifname = virTapGetRealDeviceName(net->ifname);
+
+ if (realifname == NULL) {
+ VIR_FREE(net->ifname);
+ VIR_FREE(brname);
+ return NULL;
+ }
+
+ VIR_INFO("%s -> %s", net->ifname, realifname);
+ /* hack on top of other hack: we need to set
+ * interface to 'UP' again after re-opening to find its
+ * name
+ */
+ if (virNetDevSetOnline(net->ifname, true) != 0) {
+ VIR_FREE(net->ifname);
+ VIR_FREE(brname);
+ return NULL;
+ }
+
+ cmd = virCommandNew(BHYVE);
+
+ /* CPUs */
+ virCommandAddArg(cmd, "-c");
+ virCommandAddArgFormat(cmd, "%d", vm->def->vcpus);
+
+ /* Memory */
+ virCommandAddArg(cmd, "-m");
+ virCommandAddArgFormat(cmd, "%llu",
+ VIR_DIV_UP(vm->def->mem.max_balloon, 1024));
+
+ /* Options */
+ virCommandAddArg(cmd, "-A"); /* Create an ACPI table */
+ virCommandAddArg(cmd, "-I"); /* Present ioapic to the guest */
+ virCommandAddArg(cmd, "-H"); /* vmexit from guest on hlt */
+ virCommandAddArg(cmd, "-P"); /* vmexit from guest on pause */
+
+ /* Devices */
+ virCommandAddArg(cmd, "-s");
+ virCommandAddArg(cmd, "0:0,hostbridge");
+ virCommandAddArg(cmd, "-s");
+ virCommandAddArgFormat(cmd, "1:0,virtio-net,%s", realifname);
+ virCommandAddArg(cmd, "-s");
+ virCommandAddArgFormat(cmd, "2:0,ahci-hd,%s", disk->src);
+ virCommandAddArg(cmd, "-S");
+ virCommandAddArg(cmd, "31,uart");
+ virCommandAddArg(cmd, vm->def->name);
+
+ return cmd;
+}
+
+int
+virBhyveProcessStart(virConnectPtr conn,
+ bhyveConnPtr driver,
+ virDomainObjPtr vm,
+ virDomainRunningReason reason ATTRIBUTE_UNUSED)
+{
+ char *logfile = NULL;
+ int logfd = -1;
+ off_t pos = -1;
+ char ebuf[1024];
+ virCommandPtr cmd = NULL;
+ bhyveConnPtr privconn = conn->privateData;
+ int ret = -1, status;
+
+ if (vm->def->ndisks != 1) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+ _("Domain should have one and only one disk defined"));
+ return -1;
+ }
+
+ if (virAsprintf(&logfile, "%s/%s.log",
+ BHYVE_LOG_DIR, vm->def->name) < 0)
+ return -1;
+
+
+ if ((logfd = open(logfile, O_WRONLY | O_APPEND | O_CREAT,
+ S_IRUSR|S_IWUSR)) < 0) {
+ virReportSystemError(errno,
+ _("Failed to open '%s'"),
+ logfile);
+ goto cleanup;
+ }
+
+ /* Call bhyveload to load a VM */
+ if (!(cmd = virBhyveProcessBuildLoadCmd(driver,
+ vm)))
+ goto cleanup;
+ virCommandSetOutputFD(cmd, &logfd);
+ virCommandSetErrorFD(cmd, &logfd);
+
+ /* Log generated command line */
+ virCommandWriteArgLog(cmd, logfd);
+ if ((pos = lseek(logfd, 0, SEEK_END)) < 0)
+ VIR_WARN("Unable to seek to end of logfile: %s",
+ virStrerror(errno, ebuf, sizeof(ebuf)));
+
+ VIR_INFO("Loading domain '%s'", vm->def->name);
+ if (virCommandRun(cmd, &status) < 0)
+ goto cleanup;
+
+ if (status != 0) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("Guest failed to load: %d"), status);
+ goto cleanup;
+ }
+
+ virCommandFree(cmd);
+
+ VIR_FREE(privconn->pidfile);
+ if (!(privconn->pidfile = virPidFileBuildPath(BHYVE_STATE_DIR,
+ vm->def->name))) {
+ virReportSystemError(errno,
+ "%s", _("Failed to build pidfile
path."));
+ goto cleanup;
+ }
+
+ if (unlink(privconn->pidfile) < 0 &&
+ errno != ENOENT) {
+ virReportSystemError(errno,
+ _("Cannot remove state PID file %s"),
+ privconn->pidfile);
+ goto cleanup;
+ }
+
+ /* Call bhyve to start the VM */
+ if (!(cmd = virBhyveProcessBuildBhyveCmd(driver,
+ vm)))
+ goto cleanup;
+ virCommandSetOutputFD(cmd, &logfd);
+ virCommandSetErrorFD(cmd, &logfd);
+ virCommandWriteArgLog(cmd, logfd);
+ virCommandSetPidFile(cmd, privconn->pidfile);
+ virCommandDaemonize(cmd);
+
+ VIR_INFO("Starting domain '%s'", vm->def->name);
+ ret = virCommandRun(cmd, NULL);
+
+ if (ret == 0) {
+ if (virPidFileReadPath(privconn->pidfile, &vm->pid) < 0) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("Domain %s didn't show up"),
vm->def->name);
+ goto cleanup;
+ }
+ } else {
+ goto cleanup;
+ }
+
+cleanup:
+ VIR_FREE(logfile);
+ return ret;
+}
+
+int
+virBhyveProcessStop(bhyveConnPtr driver,
+ virDomainObjPtr vm,
+ virDomainShutoffReason reason ATTRIBUTE_UNUSED)
+{
+ size_t i;
+ int ret = -1;
+ int status;
+ virCommandPtr cmd = NULL;
+
+ /* First, try to kill 'bhyve' process */
+ if (virProcessKillPainfully(vm->pid, true) != 0)
+ VIR_WARN("Failed to gracefully stop bhyve VM '%s' (pid:
%llu)",
+ vm->def->name,
+ (unsigned long long)vm->pid);
+
+ for (i = 0; i < vm->def->nnets; i++) {
+ virDomainNetDefPtr net = vm->def->nets[i];
+ int actualType = virDomainNetGetActualType(net);
+
+ if (actualType == VIR_DOMAIN_NET_TYPE_BRIDGE) {
+ ignore_value(virNetDevBridgeRemovePort(
+ virDomainNetGetActualBridgeName(net),
+ net->ifname));
+ ignore_value(virNetDevTapDelete(net->ifname));
+ }
+ }
+
+ /* No matter if shutdown was successful or not, we
+ * need to unload the VM */
+ if (!(cmd = virBhyveProcessBuildDestroyCmd(driver, vm)))
+ goto cleanup;
+
+ if (virCommandRun(cmd, &status) < 0)
+ goto cleanup;
+
+ if (status != 0) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("Guest failed to stop: %d"), status);
+ goto cleanup;
+ }
+
+ ret = 0;
+
+cleanup:
+ virCommandFree(cmd);
+ return ret;
+}
diff --git a/src/bhyve/bhyve_process.h b/src/bhyve/bhyve_process.h
new file mode 100644
index 0000000..70afe0e
--- /dev/null
+++ b/src/bhyve/bhyve_process.h
@@ -0,0 +1,36 @@
+/*
+ * bhyve_process.h: bhyve process management
+ *
+ * Copyright (C) 2013 Roman Bogorodskiy
+ *
+ * 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/>.
+ *
+ */
+
+#ifndef __BHYVE_PROCESS_H__
+# define __BHYVE_PROCESS_H__
+
+# include "bhyve_utils.h"
+
+int virBhyveProcessStart(virConnectPtr conn,
+ bhyveConnPtr driver,
+ virDomainObjPtr vm,
+ virDomainRunningReason reason);
+
+int virBhyveProcessStop(bhyveConnPtr driver,
+ virDomainObjPtr vm,
+ virDomainShutoffReason reason);
+
+#endif /* __BHYVE_PROCESS_H__ */
diff --git a/src/bhyve/bhyve_utils.h b/src/bhyve/bhyve_utils.h
new file mode 100644
index 0000000..ed503cd
--- /dev/null
+++ b/src/bhyve/bhyve_utils.h
@@ -0,0 +1,48 @@
+/*
+ * bhyve_utils.h: bhyve utils
+ *
+ * Copyright (C) 2013 Roman Bogorodskiy
+ *
+ * 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/>.
+ *
+ */
+
+#ifndef __BHYVE_UTILS_H__
+# define __BHYVE_UTILS_H__
+
+# include "driver.h"
+# include "domain_conf.h"
+# include "configmake.h"
+# include "virthread.h"
+
+# define BHYVE_CONFIG_DIR (SYSCONFDIR "/libvirt/bhyve")
+# define BHYVE_STATE_DIR (LOCALSTATEDIR "/run/libvirt/bhyve")
+# define BHYVE_LOG_DIR (LOCALSTATEDIR "/log/libvirt/bhyve")
+
+struct _bhyveConn {
+ virMutex lock;
+ virDomainObjListPtr domains;
+ virCapsPtr caps;
+ virDomainXMLOptionPtr xmlopt;
+ char *pidfile;
+};
+
+typedef struct _bhyveConn bhyveConn;
+typedef struct _bhyveConn *bhyveConnPtr;
+
+void bhyveDriverLock(bhyveConnPtr driver);
+void bhyveDriverUnlock(bhyveConnPtr driver);
+
+#endif /* __BHYVE_UTILS_H__ */
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
index e65f3e3..928f5f5 100644
--- a/src/conf/domain_conf.c
+++ b/src/conf/domain_conf.c
@@ -122,7 +122,8 @@ VIR_ENUM_IMPL(virDomainVirt, VIR_DOMAIN_VIRT_LAST,
"hyperv",
"vbox",
"phyp",
- "parallels")
+ "parallels",
+ "bhyve")
VIR_ENUM_IMPL(virDomainBoot, VIR_DOMAIN_BOOT_LAST,
"fd",
diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h
index 647d115..c82f598 100644
--- a/src/conf/domain_conf.h
+++ b/src/conf/domain_conf.h
@@ -196,6 +196,7 @@ enum virDomainVirtType {
VIR_DOMAIN_VIRT_VBOX,
VIR_DOMAIN_VIRT_PHYP,
VIR_DOMAIN_VIRT_PARALLELS,
+ VIR_DOMAIN_VIRT_BHYVE,
VIR_DOMAIN_VIRT_LAST
};
diff --git a/src/driver.h b/src/driver.h
index b6927ea..556f91f 100644
--- a/src/driver.h
+++ b/src/driver.h
@@ -46,6 +46,7 @@ typedef enum {
VIR_DRV_LIBXL = 14,
VIR_DRV_HYPERV = 15,
VIR_DRV_PARALLELS = 16,
+ VIR_DRV_BHYVE = 17,
} virDrvNo;
diff --git a/src/libvirt.c b/src/libvirt.c
index b14af7e..b493f78 100644
--- a/src/libvirt.c
+++ b/src/libvirt.c
@@ -96,6 +96,9 @@
#ifdef WITH_PARALLELS
# include "parallels/parallels_driver.h"
#endif
+#ifdef WITH_BHYVE
+# include "bhyve/bhyve_driver.h"
+#endif
#define VIR_FROM_THIS VIR_FROM_NONE
diff --git a/src/util/virerror.c b/src/util/virerror.c
index d9a9fc4..8f4d1c1 100644
--- a/src/util/virerror.c
+++ b/src/util/virerror.c
@@ -124,6 +124,7 @@ VIR_ENUM_IMPL(virErrorDomain, VIR_ERR_DOMAIN_LAST,
"Access Manager", /* 55 */
"Systemd",
+ "Bhyve",
)
--
1.8.3.2