libvirt/src/vbox/vbox_common.c
Taowei fac5d061ed vbox: Interfaces for register per parties
The patch dbb4cbf532 by Michal has splited the vbox driver into
three parties. This modification brings a more suitable interface
to the previous patch.

The new function vboxGetDriver is introduced to get the
corresponding vbox domain driver directly thought the vbox version.

Functions like vboxGetNetworkDriver and vboxGetStorageDriver
will be introduced after rewriting it's drivers.

This patch, by the way, fixed the align problem for vbox in
Makefile.am
2014-10-03 10:43:03 +02:00

7565 lines
268 KiB
C

/*
* Copyright 2014, Taowei Luo (uaedante@gmail.com)
*
* 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 <unistd.h>
#include <fcntl.h>
#include "internal.h"
#include "datatypes.h"
#include "domain_conf.h"
#include "domain_event.h"
#include "virlog.h"
#include "viralloc.h"
#include "nodeinfo.h"
#include "virstring.h"
#include "virfile.h"
#include "virtime.h"
#include "snapshot_conf.h"
#include "vbox_snapshot_conf.h"
#include "fdstream.h"
#include "configmake.h"
#include "vbox_common.h"
#include "vbox_uniformed_api.h"
#include "vbox_get_driver.h"
/* Common codes for vbox driver. With the definitions in vbox_common.h,
* it treats vbox structs as a void*. Though vboxUniformedAPI
* it call vbox functions. This file is a high level implement about
* the vbox driver.
*/
#define VIR_FROM_THIS VIR_FROM_VBOX
VIR_LOG_INIT("vbox.vbox_common");
#define RC_SUCCEEDED(rc) NS_SUCCEEDED(rc.resultCode)
#define RC_FAILED(rc) NS_FAILED(rc.resultCode)
#define VBOX_UTF16_FREE(arg) \
do { \
if (arg) { \
gVBoxAPI.UPFN.Utf16Free(data->pFuncs, arg); \
(arg) = NULL; \
} \
} while (0)
#define VBOX_UTF8_FREE(arg) \
do { \
if (arg) { \
gVBoxAPI.UPFN.Utf8Free(data->pFuncs, arg); \
(arg) = NULL; \
} \
} while (0)
#define VBOX_COM_UNALLOC_MEM(arg) \
do { \
if (arg) { \
gVBoxAPI.UPFN.ComUnallocMem(data->pFuncs, arg); \
(arg) = NULL; \
} \
} while (0)
#define VBOX_UTF16_TO_UTF8(arg1, arg2) gVBoxAPI.UPFN.Utf16ToUtf8(data->pFuncs, arg1, arg2)
#define VBOX_UTF8_TO_UTF16(arg1, arg2) gVBoxAPI.UPFN.Utf8ToUtf16(data->pFuncs, arg1, arg2)
#define VBOX_ADDREF(arg) gVBoxAPI.nsUISupports.AddRef((void *)(arg))
#define VBOX_RELEASE(arg) \
do { \
if (arg) { \
gVBoxAPI.nsUISupports.Release((void *)arg); \
(arg) = NULL; \
} \
} while (0)
#define VBOX_MEDIUM_RELEASE(arg) \
do { \
if (arg) { \
gVBoxAPI.UIMedium.Release(arg); \
(arg) = NULL; \
} \
} while (0)
#define VBOX_OBJECT_CHECK(conn, type, value) \
vboxGlobalData *data = conn->privateData;\
type ret = value;\
if (!data->vboxObj) {\
return ret;\
}
#define vboxIIDUnalloc(iid) gVBoxAPI.UIID.vboxIIDUnalloc(data, iid)
#define vboxIIDToUUID(iid, uuid) gVBoxAPI.UIID.vboxIIDToUUID(data, iid, uuid)
#define vboxIIDFromUUID(iid, uuid) gVBoxAPI.UIID.vboxIIDFromUUID(data, iid, uuid)
#define vboxIIDIsEqual(iid1, iid2) gVBoxAPI.UIID.vboxIIDIsEqual(data, iid1, iid2)
#define DEBUGIID(msg, iid) gVBoxAPI.UIID.DEBUGIID(msg, iid)
#define vboxIIDFromArrayItem(iid, array, idx) \
gVBoxAPI.UIID.vboxIIDFromArrayItem(data, iid, array, idx)
#define VBOX_IID_INITIALIZE(iid) gVBoxAPI.UIID.vboxIIDInitialize(iid)
#define ARRAY_GET_MACHINES \
(gVBoxAPI.UArray.handleGetMachines(data->vboxObj))
/* global vbox API, used for all common codes. */
static vboxUniformedAPI gVBoxAPI;
static int openSessionForMachine(vboxGlobalData *data, const unsigned char *dom_uuid, vboxIIDUnion *iid,
IMachine **machine, bool checkflag)
{
VBOX_IID_INITIALIZE(iid);
vboxIIDFromUUID(iid, dom_uuid);
if (!checkflag || gVBoxAPI.getMachineForSession) {
/* Get machine for the call to VBOX_SESSION_OPEN_EXISTING */
if (NS_FAILED(gVBoxAPI.UIVirtualBox.GetMachine(data->vboxObj, iid, machine))) {
virReportError(VIR_ERR_NO_DOMAIN, "%s",
_("no domain with matching uuid"));
return -1;
}
}
return 0;
}
/**
* function to get the values for max port per
* instance and max slots per port for the devices
*
* @returns true on Success, false on failure.
* @param vbox Input IVirtualBox pointer
* @param maxPortPerInst Output array of max port per instance
* @param maxSlotPerPort Output array of max slot per port
*
*/
static bool vboxGetMaxPortSlotValues(IVirtualBox *vbox,
PRUint32 *maxPortPerInst,
PRUint32 *maxSlotPerPort)
{
ISystemProperties *sysProps = NULL;
if (!vbox)
return false;
gVBoxAPI.UIVirtualBox.GetSystemProperties(vbox, &sysProps);
if (!sysProps)
return false;
gVBoxAPI.UISystemProperties.GetMaxPortCountForStorageBus(sysProps,
StorageBus_IDE,
&maxPortPerInst[StorageBus_IDE]);
gVBoxAPI.UISystemProperties.GetMaxPortCountForStorageBus(sysProps,
StorageBus_SATA,
&maxPortPerInst[StorageBus_SATA]);
gVBoxAPI.UISystemProperties.GetMaxPortCountForStorageBus(sysProps,
StorageBus_SCSI,
&maxPortPerInst[StorageBus_SCSI]);
gVBoxAPI.UISystemProperties.GetMaxPortCountForStorageBus(sysProps,
StorageBus_Floppy,
&maxPortPerInst[StorageBus_Floppy]);
gVBoxAPI.UISystemProperties.GetMaxDevicesPerPortForStorageBus(sysProps,
StorageBus_IDE,
&maxSlotPerPort[StorageBus_IDE]);
gVBoxAPI.UISystemProperties.GetMaxDevicesPerPortForStorageBus(sysProps,
StorageBus_SATA,
&maxSlotPerPort[StorageBus_SATA]);
gVBoxAPI.UISystemProperties.GetMaxDevicesPerPortForStorageBus(sysProps,
StorageBus_SCSI,
&maxSlotPerPort[StorageBus_SCSI]);
gVBoxAPI.UISystemProperties.GetMaxDevicesPerPortForStorageBus(sysProps,
StorageBus_Floppy,
&maxSlotPerPort[StorageBus_Floppy]);
VBOX_RELEASE(sysProps);
return true;
}
/**
* function to get the StorageBus, Port number
* and Device number for the given devicename
* e.g: hda has StorageBus = IDE, port = 0,
* device = 0
*
* @returns true on Success, false on failure.
* @param deviceName Input device name
* @param aMaxPortPerInst Input array of max port per device instance
* @param aMaxSlotPerPort Input array of max slot per device port
* @param storageBus Input storage bus type
* @param deviceInst Output device instance number
* @param devicePort Output port number
* @param deviceSlot Output slot number
*
*/
static bool vboxGetDeviceDetails(const char *deviceName,
PRUint32 *aMaxPortPerInst,
PRUint32 *aMaxSlotPerPort,
PRUint32 storageBus,
PRInt32 *deviceInst,
PRInt32 *devicePort,
PRInt32 *deviceSlot) {
int total = 0;
PRUint32 maxPortPerInst = 0;
PRUint32 maxSlotPerPort = 0;
if (!deviceName ||
!deviceInst ||
!devicePort ||
!deviceSlot ||
!aMaxPortPerInst ||
!aMaxSlotPerPort)
return false;
if ((storageBus < StorageBus_IDE) ||
(storageBus > StorageBus_Floppy))
return false;
total = virDiskNameToIndex(deviceName);
maxPortPerInst = aMaxPortPerInst[storageBus];
maxSlotPerPort = aMaxSlotPerPort[storageBus];
if (!maxPortPerInst ||
!maxSlotPerPort ||
(total < 0))
return false;
*deviceInst = total / (maxPortPerInst * maxSlotPerPort);
*devicePort = (total % (maxPortPerInst * maxSlotPerPort)) / maxSlotPerPort;
*deviceSlot = (total % (maxPortPerInst * maxSlotPerPort)) % maxSlotPerPort;
VIR_DEBUG("name=%s, total=%d, storageBus=%u, deviceInst=%d, "
"devicePort=%d deviceSlot=%d, maxPortPerInst=%u maxSlotPerPort=%u",
deviceName, total, storageBus, *deviceInst, *devicePort,
*deviceSlot, maxPortPerInst, maxSlotPerPort);
return true;
}
/**
* function to generate the name for medium,
* for e.g: hda, sda, etc
*
* @returns null terminated string with device name or NULL
* for failures
* @param conn Input Connection Pointer
* @param storageBus Input storage bus type
* @param deviceInst Input device instance number
* @param devicePort Input port number
* @param deviceSlot Input slot number
* @param aMaxPortPerInst Input array of max port per device instance
* @param aMaxSlotPerPort Input array of max slot per device port
*
*/
static char *vboxGenerateMediumName(PRUint32 storageBus,
PRInt32 deviceInst,
PRInt32 devicePort,
PRInt32 deviceSlot,
PRUint32 *aMaxPortPerInst,
PRUint32 *aMaxSlotPerPort)
{
const char *prefix = NULL;
char *name = NULL;
int total = 0;
PRUint32 maxPortPerInst = 0;
PRUint32 maxSlotPerPort = 0;
if (!aMaxPortPerInst ||
!aMaxSlotPerPort)
return NULL;
if ((storageBus < StorageBus_IDE) ||
(storageBus > StorageBus_Floppy))
return NULL;
maxPortPerInst = aMaxPortPerInst[storageBus];
maxSlotPerPort = aMaxSlotPerPort[storageBus];
total = (deviceInst * maxPortPerInst * maxSlotPerPort)
+ (devicePort * maxSlotPerPort)
+ deviceSlot;
if (storageBus == StorageBus_IDE) {
prefix = "hd";
} else if ((storageBus == StorageBus_SATA) ||
(storageBus == StorageBus_SCSI)) {
prefix = "sd";
} else if (storageBus == StorageBus_Floppy) {
prefix = "fd";
}
name = virIndexToDiskName(total, prefix);
VIR_DEBUG("name=%s, total=%d, storageBus=%u, deviceInst=%d, "
"devicePort=%d deviceSlot=%d, maxPortPerInst=%u maxSlotPerPort=%u",
NULLSTR(name), total, storageBus, deviceInst, devicePort,
deviceSlot, maxPortPerInst, maxSlotPerPort);
return name;
}
static virDomainDefParserConfig vboxDomainDefParserConfig = {
.macPrefix = { 0x08, 0x00, 0x27 },
};
static virDomainXMLOptionPtr
vboxXMLConfInit(void)
{
return virDomainXMLOptionNew(&vboxDomainDefParserConfig,
NULL, NULL);
}
static int vboxInitialize(vboxGlobalData *data)
{
if (gVBoxAPI.UPFN.Initialize(data) != 0)
goto cleanup;
if (gVBoxAPI.domainEventCallbacks && gVBoxAPI.initializeDomainEvent(data) != 0)
goto cleanup;
if (data->vboxObj == NULL) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("IVirtualBox object is null"));
goto cleanup;
}
if (data->vboxSession == NULL) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("ISession object is null"));
goto cleanup;
}
return 0;
cleanup:
return -1;
}
static virCapsPtr vboxCapsInit(void)
{
virCapsPtr caps;
virCapsGuestPtr guest;
if ((caps = virCapabilitiesNew(virArchFromHost(),
false, false)) == NULL)
goto no_memory;
if (nodeCapsInitNUMA(caps) < 0)
goto no_memory;
if ((guest = virCapabilitiesAddGuest(caps,
"hvm",
caps->host.arch,
NULL,
NULL,
0,
NULL)) == NULL)
goto no_memory;
if (virCapabilitiesAddGuestDomain(guest,
"vbox",
NULL,
NULL,
0,
NULL) == NULL)
goto no_memory;
return caps;
no_memory:
virObjectUnref(caps);
return NULL;
}
static int vboxExtractVersion(vboxGlobalData *data)
{
int ret = -1;
PRUnichar *versionUtf16 = NULL;
char *vboxVersion = NULL;
nsresult rc;
if (data->version > 0)
return 0;
rc = gVBoxAPI.UIVirtualBox.GetVersion(data->vboxObj, &versionUtf16);
if (NS_FAILED(rc))
goto failed;
VBOX_UTF16_TO_UTF8(versionUtf16, &vboxVersion);
if (virParseVersionString(vboxVersion, &data->version, false) >= 0)
ret = 0;
VBOX_UTF8_FREE(vboxVersion);
VBOX_COM_UNALLOC_MEM(versionUtf16);
failed:
if (ret != 0)
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Could not extract VirtualBox version"));
return ret;
}
static void vboxUninitialize(vboxGlobalData *data)
{
if (!data)
return;
gVBoxAPI.UPFN.Uninitialize(data);
virObjectUnref(data->caps);
virObjectUnref(data->xmlopt);
if (gVBoxAPI.domainEventCallbacks)
virObjectEventStateFree(data->domainEvents);
VIR_FREE(data);
}
static virDrvOpenStatus
vboxConnectOpen(virConnectPtr conn,
virConnectAuthPtr auth ATTRIBUTE_UNUSED,
unsigned int flags)
{
vboxGlobalData *data = NULL;
uid_t uid = geteuid();
virCheckFlags(VIR_CONNECT_RO, VIR_DRV_OPEN_ERROR);
if (conn->uri == NULL &&
!(conn->uri = virURIParse(uid ? "vbox:///session" : "vbox:///system")))
return VIR_DRV_OPEN_ERROR;
if (conn->uri->scheme == NULL ||
STRNEQ(conn->uri->scheme, "vbox"))
return VIR_DRV_OPEN_DECLINED;
/* Leave for remote driver */
if (conn->uri->server != NULL)
return VIR_DRV_OPEN_DECLINED;
if (conn->uri->path == NULL || STREQ(conn->uri->path, "")) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("no VirtualBox driver path specified (try vbox:///session)"));
return VIR_DRV_OPEN_ERROR;
}
if (uid != 0) {
if (STRNEQ(conn->uri->path, "/session")) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("unknown driver path '%s' specified (try vbox:///session)"), conn->uri->path);
return VIR_DRV_OPEN_ERROR;
}
} else { /* root */
if (STRNEQ(conn->uri->path, "/system") &&
STRNEQ(conn->uri->path, "/session")) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("unknown driver path '%s' specified (try vbox:///system)"), conn->uri->path);
return VIR_DRV_OPEN_ERROR;
}
}
if (VIR_ALLOC(data) < 0)
return VIR_DRV_OPEN_ERROR;
if (!(data->caps = vboxCapsInit()) ||
vboxInitialize(data) < 0 ||
vboxExtractVersion(data) < 0 ||
!(data->xmlopt = vboxXMLConfInit())) {
vboxUninitialize(data);
return VIR_DRV_OPEN_ERROR;
}
if (gVBoxAPI.domainEventCallbacks) {
if (!(data->domainEvents = virObjectEventStateNew())) {
vboxUninitialize(data);
return VIR_DRV_OPEN_ERROR;
}
data->conn = conn;
}
if (gVBoxAPI.hasStaticGlobalData)
gVBoxAPI.registerGlobalData(data);
conn->privateData = data;
VIR_DEBUG("in vboxOpen");
return VIR_DRV_OPEN_SUCCESS;
}
static int vboxConnectClose(virConnectPtr conn)
{
vboxGlobalData *data = conn->privateData;
VIR_DEBUG("%s: in vboxClose", conn->driver->name);
vboxUninitialize(data);
conn->privateData = NULL;
return 0;
}
static int
vboxDomainSave(virDomainPtr dom, const char *path ATTRIBUTE_UNUSED)
{
VBOX_OBJECT_CHECK(dom->conn, int, -1);
IConsole *console = NULL;
vboxIIDUnion iid;
IMachine *machine = NULL;
IProgress *progress = NULL;
resultCodeUnion resultCode;
nsresult rc;
/* VirtualBox currently doesn't support saving to a file
* at a location other then the machine folder and thus
* setting path to ATTRIBUTE_UNUSED for now, will change
* this behaviour once get the VirtualBox API in right
* shape to do this
*/
/* Open a Session for the machine */
if (openSessionForMachine(data, dom->uuid, &iid, &machine, true) < 0)
goto cleanup;
rc = gVBoxAPI.UISession.OpenExisting(data, &iid, machine);
if (NS_FAILED(rc))
goto cleanup;
rc = gVBoxAPI.UISession.GetConsole(data->vboxSession, &console);
if (NS_FAILED(rc) || !console)
goto freeSession;
rc = gVBoxAPI.UIConsole.SaveState(console, &progress);
if (!progress)
goto freeSession;
gVBoxAPI.UIProgress.WaitForCompletion(progress, -1);
gVBoxAPI.UIProgress.GetResultCode(progress, &resultCode);
if (RC_SUCCEEDED(resultCode))
ret = 0;
freeSession:
gVBoxAPI.UISession.Close(data->vboxSession);
cleanup:
DEBUGIID("UUID of machine being saved:", &iid);
VBOX_RELEASE(machine);
VBOX_RELEASE(console);
VBOX_RELEASE(progress);
vboxIIDUnalloc(&iid);
return ret;
}
static void vboxDriverLock(vboxGlobalData *data)
{
virMutexLock(&data->lock);
}
static void vboxDriverUnlock(vboxGlobalData *data)
{
virMutexUnlock(&data->lock);
}
static int vboxConnectGetVersion(virConnectPtr conn, unsigned long *version)
{
vboxGlobalData *data = conn->privateData;
VIR_DEBUG("%s: in vboxGetVersion", conn->driver->name);
vboxDriverLock(data);
*version = data->version;
vboxDriverUnlock(data);
return 0;
}
static char *vboxConnectGetHostname(virConnectPtr conn ATTRIBUTE_UNUSED)
{
return virGetHostname();
}
static int vboxConnectIsSecure(virConnectPtr conn ATTRIBUTE_UNUSED)
{
/* Driver is using local, non-network based transport */
return 1;
}
static int vboxConnectIsEncrypted(virConnectPtr conn ATTRIBUTE_UNUSED)
{
/* No encryption is needed, or used on the local transport*/
return 0;
}
static int vboxConnectIsAlive(virConnectPtr conn ATTRIBUTE_UNUSED)
{
return 1;
}
static int
vboxConnectGetMaxVcpus(virConnectPtr conn, const char *type ATTRIBUTE_UNUSED)
{
VBOX_OBJECT_CHECK(conn, int, -1);
PRUint32 maxCPUCount = 0;
/* VirtualBox Supports only hvm and thus the type passed to it
* has no meaning, setting it to ATTRIBUTE_UNUSED
*/
ISystemProperties *systemProperties = NULL;
gVBoxAPI.UIVirtualBox.GetSystemProperties(data->vboxObj, &systemProperties);
if (!systemProperties)
goto cleanup;
gVBoxAPI.UISystemProperties.GetMaxGuestCPUCount(systemProperties, &maxCPUCount);
if (maxCPUCount > 0)
ret = maxCPUCount;
cleanup:
VBOX_RELEASE(systemProperties);
return ret;
}
static char *vboxConnectGetCapabilities(virConnectPtr conn)
{
VBOX_OBJECT_CHECK(conn, char *, NULL);
vboxDriverLock(data);
ret = virCapabilitiesFormatXML(data->caps);
vboxDriverUnlock(data);
return ret;
}
static int vboxConnectListDomains(virConnectPtr conn, int *ids, int nids)
{
VBOX_OBJECT_CHECK(conn, int, -1);
vboxArray machines = VBOX_ARRAY_INITIALIZER;
PRUint32 state;
nsresult rc;
size_t i, j;
rc = gVBoxAPI.UArray.vboxArrayGet(&machines, data->vboxObj, ARRAY_GET_MACHINES);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Could not get list of Domains, rc=%08x"),
(unsigned)rc);
goto cleanup;
}
ret = 0;
for (i = 0, j = 0; (i < machines.count) && (j < nids); ++i) {
IMachine *machine = machines.items[i];
if (machine) {
PRBool isAccessible = PR_FALSE;
gVBoxAPI.UIMachine.GetAccessible(machine, &isAccessible);
if (isAccessible) {
gVBoxAPI.UIMachine.GetState(machine, &state);
if (gVBoxAPI.machineStateChecker.Online(state)) {
ret++;
ids[j++] = i + 1;
}
}
}
}
cleanup:
gVBoxAPI.UArray.vboxArrayRelease(&machines);
return ret;
}
static int vboxConnectNumOfDomains(virConnectPtr conn)
{
VBOX_OBJECT_CHECK(conn, int, -1);
vboxArray machines = VBOX_ARRAY_INITIALIZER;
PRUint32 state;
nsresult rc;
size_t i;
rc = gVBoxAPI.UArray.vboxArrayGet(&machines, data->vboxObj, ARRAY_GET_MACHINES);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Could not get number of Domains, rc=%08x"), (unsigned)rc);
goto cleanup;
}
ret = 0;
for (i = 0; i < machines.count; ++i) {
IMachine *machine = machines.items[i];
if (machine) {
PRBool isAccessible = PR_FALSE;
gVBoxAPI.UIMachine.GetAccessible(machine, &isAccessible);
if (isAccessible) {
gVBoxAPI.UIMachine.GetState(machine, &state);
if (gVBoxAPI.machineStateChecker.Online(state))
ret++;
}
}
}
cleanup:
gVBoxAPI.UArray.vboxArrayRelease(&machines);
return ret;
}
static virDomainPtr vboxDomainLookupByID(virConnectPtr conn, int id)
{
VBOX_OBJECT_CHECK(conn, virDomainPtr, NULL);
vboxArray machines = VBOX_ARRAY_INITIALIZER;
IMachine *machine;
PRBool isAccessible = PR_FALSE;
PRUnichar *machineNameUtf16 = NULL;
char *machineNameUtf8 = NULL;
vboxIIDUnion iid;
unsigned char uuid[VIR_UUID_BUFLEN];
PRUint32 state;
nsresult rc;
VBOX_IID_INITIALIZE(&iid);
/* Internal vbox IDs start from 0, the public libvirt ID
* starts from 1, so refuse id == 0, and adjust the rest*/
if (id == 0) {
virReportError(VIR_ERR_NO_DOMAIN,
_("no domain with matching id %d"), id);
return NULL;
}
id = id - 1;
rc = gVBoxAPI.UArray.vboxArrayGet(&machines, data->vboxObj, ARRAY_GET_MACHINES);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Could not get list of machines, rc=%08x"), (unsigned)rc);
return NULL;
}
if (id >= machines.count)
goto cleanup;
machine = machines.items[id];
if (!machine)
goto cleanup;
isAccessible = PR_FALSE;
gVBoxAPI.UIMachine.GetAccessible(machine, &isAccessible);
if (!isAccessible)
goto cleanup;
gVBoxAPI.UIMachine.GetState(machine, &state);
if (!gVBoxAPI.machineStateChecker.Online(state))
goto cleanup;
gVBoxAPI.UIMachine.GetName(machine, &machineNameUtf16);
VBOX_UTF16_TO_UTF8(machineNameUtf16, &machineNameUtf8);
gVBoxAPI.UIMachine.GetId(machine, &iid);
vboxIIDToUUID(&iid, uuid);
vboxIIDUnalloc(&iid);
/* get a new domain pointer from virGetDomain, if it fails
* then no need to assign the id, else assign the id, cause
* it is -1 by default. rest is taken care by virGetDomain
* itself, so need not worry.
*/
ret = virGetDomain(conn, machineNameUtf8, uuid);
if (ret)
ret->id = id + 1;
/* Cleanup all the XPCOM allocated stuff here */
VBOX_UTF8_FREE(machineNameUtf8);
VBOX_UTF16_FREE(machineNameUtf16);
cleanup:
gVBoxAPI.UArray.vboxArrayRelease(&machines);
return ret;
}
virDomainPtr vboxDomainLookupByUUID(virConnectPtr conn,
const unsigned char *uuid)
{
VBOX_OBJECT_CHECK(conn, virDomainPtr, NULL);
vboxArray machines = VBOX_ARRAY_INITIALIZER;
vboxIIDUnion iid;
char *machineNameUtf8 = NULL;
PRUnichar *machineNameUtf16 = NULL;
unsigned char iid_as_uuid[VIR_UUID_BUFLEN];
size_t i;
bool matched = false;
nsresult rc;
VBOX_IID_INITIALIZE(&iid);
rc = gVBoxAPI.UArray.vboxArrayGet(&machines, data->vboxObj, ARRAY_GET_MACHINES);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Could not get list of machines, rc=%08x"), (unsigned)rc);
return NULL;
}
for (i = 0; i < machines.count; ++i) {
IMachine *machine = machines.items[i];
PRBool isAccessible = PR_FALSE;
if (!machine)
continue;
gVBoxAPI.UIMachine.GetAccessible(machine, &isAccessible);
if (!isAccessible)
continue;
rc = gVBoxAPI.UIMachine.GetId(machine, &iid);
if (NS_FAILED(rc))
continue;
vboxIIDToUUID(&iid, iid_as_uuid);
vboxIIDUnalloc(&iid);
if (memcmp(uuid, iid_as_uuid, VIR_UUID_BUFLEN) == 0) {
PRUint32 state;
matched = true;
gVBoxAPI.UIMachine.GetName(machine, &machineNameUtf16);
VBOX_UTF16_TO_UTF8(machineNameUtf16, &machineNameUtf8);
gVBoxAPI.UIMachine.GetState(machine, &state);
/* get a new domain pointer from virGetDomain, if it fails
* then no need to assign the id, else assign the id, cause
* it is -1 by default. rest is taken care by virGetDomain
* itself, so need not worry.
*/
ret = virGetDomain(conn, machineNameUtf8, iid_as_uuid);
if (ret &&
gVBoxAPI.machineStateChecker.Online(state))
ret->id = i + 1;
}
if (matched)
break;
}
/* Do the cleanup and take care you dont leak any memory */
VBOX_UTF8_FREE(machineNameUtf8);
VBOX_COM_UNALLOC_MEM(machineNameUtf16);
gVBoxAPI.UArray.vboxArrayRelease(&machines);
return ret;
}
static virDomainPtr
vboxDomainLookupByName(virConnectPtr conn, const char *name)
{
VBOX_OBJECT_CHECK(conn, virDomainPtr, NULL);
vboxArray machines = VBOX_ARRAY_INITIALIZER;
vboxIIDUnion iid;
char *machineNameUtf8 = NULL;
PRUnichar *machineNameUtf16 = NULL;
unsigned char uuid[VIR_UUID_BUFLEN];
size_t i;
bool matched = false;
nsresult rc;
VBOX_IID_INITIALIZE(&iid);
rc = gVBoxAPI.UArray.vboxArrayGet(&machines, data->vboxObj, ARRAY_GET_MACHINES);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Could not get list of machines, rc=%08x"), (unsigned)rc);
return NULL;
}
for (i = 0; i < machines.count; ++i) {
IMachine *machine = machines.items[i];
PRBool isAccessible = PR_FALSE;
if (!machine)
continue;
gVBoxAPI.UIMachine.GetAccessible(machine, &isAccessible);
if (!isAccessible)
continue;
gVBoxAPI.UIMachine.GetName(machine, &machineNameUtf16);
VBOX_UTF16_TO_UTF8(machineNameUtf16, &machineNameUtf8);
if (STREQ(name, machineNameUtf8)) {
PRUint32 state;
matched = true;
gVBoxAPI.UIMachine.GetId(machine, &iid);
vboxIIDToUUID(&iid, uuid);
vboxIIDUnalloc(&iid);
gVBoxAPI.UIMachine.GetState(machine, &state);
/* get a new domain pointer from virGetDomain, if it fails
* then no need to assign the id, else assign the id, cause
* it is -1 by default. rest is taken care by virGetDomain
* itself, so need not worry.
*/
ret = virGetDomain(conn, machineNameUtf8, uuid);
if (ret &&
gVBoxAPI.machineStateChecker.Online(state))
ret->id = i + 1;
}
VBOX_UTF8_FREE(machineNameUtf8);
VBOX_COM_UNALLOC_MEM(machineNameUtf16);
if (matched)
break;
}
gVBoxAPI.UArray.vboxArrayRelease(&machines);
return ret;
}
static void
vboxSetBootDeviceOrder(virDomainDefPtr def, vboxGlobalData *data,
IMachine *machine)
{
ISystemProperties *systemProperties = NULL;
PRUint32 maxBootPosition = 0;
size_t i = 0;
VIR_DEBUG("def->os.type %s", def->os.type);
VIR_DEBUG("def->os.arch %s", virArchToString(def->os.arch));
VIR_DEBUG("def->os.machine %s", def->os.machine);
VIR_DEBUG("def->os.nBootDevs %zu", def->os.nBootDevs);
VIR_DEBUG("def->os.bootDevs[0] %d", def->os.bootDevs[0]);
VIR_DEBUG("def->os.bootDevs[1] %d", def->os.bootDevs[1]);
VIR_DEBUG("def->os.bootDevs[2] %d", def->os.bootDevs[2]);
VIR_DEBUG("def->os.bootDevs[3] %d", def->os.bootDevs[3]);
VIR_DEBUG("def->os.init %s", def->os.init);
VIR_DEBUG("def->os.kernel %s", def->os.kernel);
VIR_DEBUG("def->os.initrd %s", def->os.initrd);
VIR_DEBUG("def->os.cmdline %s", def->os.cmdline);
VIR_DEBUG("def->os.root %s", def->os.root);
if (def->os.loader) {
VIR_DEBUG("def->os.loader->path %s", def->os.loader->path);
VIR_DEBUG("def->os.loader->readonly %d", def->os.loader->readonly);
VIR_DEBUG("def->os.loader->type %d", def->os.loader->type);
VIR_DEBUG("def->os.loader->nvram %s", def->os.loader->nvram);
}
VIR_DEBUG("def->os.bootloader %s", def->os.bootloader);
VIR_DEBUG("def->os.bootloaderArgs %s", def->os.bootloaderArgs);
gVBoxAPI.UIVirtualBox.GetSystemProperties(data->vboxObj, &systemProperties);
if (systemProperties) {
gVBoxAPI.UISystemProperties.GetMaxBootPosition(systemProperties,
&maxBootPosition);
VBOX_RELEASE(systemProperties);
}
/* Clear the defaults first */
for (i = 0; i < maxBootPosition; i++) {
gVBoxAPI.UIMachine.SetBootOrder(machine, i+1, DeviceType_Null);
}
for (i = 0; (i < def->os.nBootDevs) && (i < maxBootPosition); i++) {
PRUint32 device = DeviceType_Null;
if (def->os.bootDevs[i] == VIR_DOMAIN_BOOT_FLOPPY) {
device = DeviceType_Floppy;
} else if (def->os.bootDevs[i] == VIR_DOMAIN_BOOT_CDROM) {
device = DeviceType_DVD;
} else if (def->os.bootDevs[i] == VIR_DOMAIN_BOOT_DISK) {
device = DeviceType_HardDisk;
} else if (def->os.bootDevs[i] == VIR_DOMAIN_BOOT_NET) {
device = DeviceType_Network;
}
gVBoxAPI.UIMachine.SetBootOrder(machine, i+1, device);
}
}
static void
vboxAttachDrivesNew(virDomainDefPtr def, vboxGlobalData *data, IMachine *machine)
{
/* AttachDrives for 3.0 and later */
size_t i;
nsresult rc;
PRUint32 maxPortPerInst[StorageBus_Floppy + 1] = {};
PRUint32 maxSlotPerPort[StorageBus_Floppy + 1] = {};
PRUnichar *storageCtlName = NULL;
bool error = false;
if (gVBoxAPI.vboxAttachDrivesUseOld)
VIR_WARN("This function may not work in current vbox version");
/* get the max port/slots/etc for the given storage bus */
error = !vboxGetMaxPortSlotValues(data->vboxObj, maxPortPerInst,
maxSlotPerPort);
/* add a storage controller for the mediums to be attached */
/* this needs to change when multiple controller are supported for
* ver > 3.1 */
{
IStorageController *storageCtl = NULL;
PRUnichar *sName = NULL;
VBOX_UTF8_TO_UTF16("IDE Controller", &sName);
gVBoxAPI.UIMachine.AddStorageController(machine,
sName,
StorageBus_IDE,
&storageCtl);
VBOX_UTF16_FREE(sName);
VBOX_RELEASE(storageCtl);
VBOX_UTF8_TO_UTF16("SATA Controller", &sName);
gVBoxAPI.UIMachine.AddStorageController(machine,
sName,
StorageBus_SATA,
&storageCtl);
VBOX_UTF16_FREE(sName);
VBOX_RELEASE(storageCtl);
VBOX_UTF8_TO_UTF16("SCSI Controller", &sName);
gVBoxAPI.UIMachine.AddStorageController(machine,
sName,
StorageBus_SCSI,
&storageCtl);
VBOX_UTF16_FREE(sName);
VBOX_RELEASE(storageCtl);
VBOX_UTF8_TO_UTF16("Floppy Controller", &sName);
gVBoxAPI.UIMachine.AddStorageController(machine,
sName,
StorageBus_Floppy,
&storageCtl);
VBOX_UTF16_FREE(sName);
VBOX_RELEASE(storageCtl);
}
for (i = 0; i < def->ndisks && !error; i++) {
const char *src = virDomainDiskGetSource(def->disks[i]);
int type = virDomainDiskGetType(def->disks[i]);
int format = virDomainDiskGetFormat(def->disks[i]);
VIR_DEBUG("disk(%zu) type: %d", i, type);
VIR_DEBUG("disk(%zu) device: %d", i, def->disks[i]->device);
VIR_DEBUG("disk(%zu) bus: %d", i, def->disks[i]->bus);
VIR_DEBUG("disk(%zu) src: %s", i, src);
VIR_DEBUG("disk(%zu) dst: %s", i, def->disks[i]->dst);
VIR_DEBUG("disk(%zu) driverName: %s", i,
virDomainDiskGetDriver(def->disks[i]));
VIR_DEBUG("disk(%zu) driverType: %s", i,
virStorageFileFormatTypeToString(format));
VIR_DEBUG("disk(%zu) cachemode: %d", i, def->disks[i]->cachemode);
VIR_DEBUG("disk(%zu) readonly: %s", i, (def->disks[i]->src->readonly
? "True" : "False"));
VIR_DEBUG("disk(%zu) shared: %s", i, (def->disks[i]->src->shared
? "True" : "False"));
if (type == VIR_STORAGE_TYPE_FILE && src) {
IMedium *medium = NULL;
vboxIIDUnion mediumUUID;
PRUnichar *mediumFileUtf16 = NULL;
PRUint32 storageBus = StorageBus_Null;
PRUint32 deviceType = DeviceType_Null;
PRUint32 accessMode = AccessMode_ReadOnly;
PRInt32 deviceInst = 0;
PRInt32 devicePort = 0;
PRInt32 deviceSlot = 0;
VBOX_IID_INITIALIZE(&mediumUUID);
VBOX_UTF8_TO_UTF16(src, &mediumFileUtf16);
if (def->disks[i]->device == VIR_DOMAIN_DISK_DEVICE_DISK) {
deviceType = DeviceType_HardDisk;
accessMode = AccessMode_ReadWrite;
} else if (def->disks[i]->device == VIR_DOMAIN_DISK_DEVICE_CDROM) {
deviceType = DeviceType_DVD;
accessMode = AccessMode_ReadOnly;
} else if (def->disks[i]->device == VIR_DOMAIN_DISK_DEVICE_FLOPPY) {
deviceType = DeviceType_Floppy;
accessMode = AccessMode_ReadWrite;
} else {
VBOX_UTF16_FREE(mediumFileUtf16);
continue;
}
gVBoxAPI.UIVirtualBox.FindMedium(data->vboxObj, mediumFileUtf16,
deviceType, accessMode, &medium);
if (!medium) {
PRUnichar *mediumEmpty = NULL;
VBOX_UTF8_TO_UTF16("", &mediumEmpty);
rc = gVBoxAPI.UIVirtualBox.OpenMedium(data->vboxObj,
mediumFileUtf16,
deviceType, accessMode,
&medium);
VBOX_UTF16_FREE(mediumEmpty);
}
if (!medium) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Failed to attach the following disk/dvd/floppy "
"to the machine: %s, rc=%08x"),
src, (unsigned)rc);
VBOX_UTF16_FREE(mediumFileUtf16);
continue;
}
rc = gVBoxAPI.UIMedium.GetId(medium, &mediumUUID);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("can't get the uuid of the file to be attached "
"as harddisk/dvd/floppy: %s, rc=%08x"),
src, (unsigned)rc);
VBOX_MEDIUM_RELEASE(medium);
VBOX_UTF16_FREE(mediumFileUtf16);
continue;
}
if (def->disks[i]->device == VIR_DOMAIN_DISK_DEVICE_DISK) {
if (def->disks[i]->src->readonly) {
gVBoxAPI.UIMedium.SetType(medium, MediumType_Immutable);
VIR_DEBUG("setting harddisk to immutable");
} else if (!def->disks[i]->src->readonly) {
gVBoxAPI.UIMedium.SetType(medium, MediumType_Normal);
VIR_DEBUG("setting harddisk type to normal");
}
}
if (def->disks[i]->bus == VIR_DOMAIN_DISK_BUS_IDE) {
VBOX_UTF8_TO_UTF16("IDE Controller", &storageCtlName);
storageBus = StorageBus_IDE;
} else if (def->disks[i]->bus == VIR_DOMAIN_DISK_BUS_SATA) {
VBOX_UTF8_TO_UTF16("SATA Controller", &storageCtlName);
storageBus = StorageBus_SATA;
} else if (def->disks[i]->bus == VIR_DOMAIN_DISK_BUS_SCSI) {
VBOX_UTF8_TO_UTF16("SCSI Controller", &storageCtlName);
storageBus = StorageBus_SCSI;
} else if (def->disks[i]->bus == VIR_DOMAIN_DISK_BUS_FDC) {
VBOX_UTF8_TO_UTF16("Floppy Controller", &storageCtlName);
storageBus = StorageBus_Floppy;
}
/* get the device details i.e instance, port and slot */
if (!vboxGetDeviceDetails(def->disks[i]->dst,
maxPortPerInst,
maxSlotPerPort,
storageBus,
&deviceInst,
&devicePort,
&deviceSlot)) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("can't get the port/slot number of "
"harddisk/dvd/floppy to be attached: "
"%s, rc=%08x"),
src, (unsigned)rc);
VBOX_MEDIUM_RELEASE(medium);
vboxIIDUnalloc(&mediumUUID);
VBOX_UTF16_FREE(mediumFileUtf16);
continue;
}
/* attach the harddisk/dvd/Floppy to the storage controller */
rc = gVBoxAPI.UIMachine.AttachDevice(machine,
storageCtlName,
devicePort,
deviceSlot,
deviceType,
medium);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("could not attach the file as "
"harddisk/dvd/floppy: %s, rc=%08x"),
src, (unsigned)rc);
} else {
DEBUGIID("Attached HDD/DVD/Floppy with UUID", &mediumUUID);
}
VBOX_MEDIUM_RELEASE(medium);
vboxIIDUnalloc(&mediumUUID);
VBOX_UTF16_FREE(mediumFileUtf16);
VBOX_UTF16_FREE(storageCtlName);
}
}
}
static void
vboxAttachDrives(virDomainDefPtr def, vboxGlobalData *data, IMachine *machine)
{
/* Here, About the vboxAttachDrives. In fact,there is
* three different implementations. We name it as
* v1, v2 and v3.
*
* The first version(v1) is only used in vbox 2.2 and 3.0,
* v2 is used by 3.1 and 3.2, and v3 is used for later
* vbox versions. In sight of implementation, the v1 is
* totally different with v2 and v3. The v2 shares the same
* outline with v3, meanwhile the API they used has much
* difference.
*
* It seems we have no thing to do with old versions such as
* v1 and v2 when developing new vbox drivers. What's more,
* most of the vbox APIs used in v1 and v2 is incompatible with
* new vbox versions. It is a burden to put these APIs into
* vboxUniformedAPI, I prefer not to do that.
*
* After balancing the code size and the complied code size,
* I put my solution here. The v1 and v2 is a version specified
* code, which only be generated for first four version. The v3
* will be put in vbox_common.c, it be complied only once, then
* be used by all next vbox drivers.
*
* Check the flag vboxAttachDrivesUseOld can tell you which
* implementation to use. When the flag is set, we need use
* the old version though gVBoxAPI.vboxAttachDrivesOld. It
* will automatically point to v1 or v2 deponds on you version.
* If the flag is clear, just call vboxAttachDrivesNew, which
* is the v3 implementation.
*/
if (gVBoxAPI.vboxAttachDrivesUseOld)
gVBoxAPI.vboxAttachDrivesOld(def, data, machine);
else
vboxAttachDrivesNew(def, data, machine);
}
static void
vboxAttachSound(virDomainDefPtr def, IMachine *machine)
{
nsresult rc;
IAudioAdapter *audioAdapter = NULL;
/* Check if def->nsounds is one as VirtualBox currently supports
* only one sound card
*/
if (def->nsounds != 1)
return;
gVBoxAPI.UIMachine.GetAudioAdapter(machine, &audioAdapter);
if (!audioAdapter)
return;
rc = gVBoxAPI.UIAudioAdapter.SetEnabled(audioAdapter, 1);
if (NS_FAILED(rc))
goto cleanup;
if (def->sounds[0]->model == VIR_DOMAIN_SOUND_MODEL_SB16) {
gVBoxAPI.UIAudioAdapter.SetAudioController(audioAdapter,
AudioControllerType_SB16);
} else if (def->sounds[0]->model == VIR_DOMAIN_SOUND_MODEL_AC97) {
gVBoxAPI.UIAudioAdapter.SetAudioController(audioAdapter,
AudioControllerType_AC97);
}
cleanup:
VBOX_RELEASE(audioAdapter);
}
static void
vboxAttachNetwork(virDomainDefPtr def, vboxGlobalData *data, IMachine *machine)
{
ISystemProperties *systemProperties = NULL;
PRUint32 chipsetType = ChipsetType_Null;
PRUint32 networkAdapterCount = 0;
size_t i = 0;
if (gVBoxAPI.chipsetType)
gVBoxAPI.UIMachine.GetChipsetType(machine, &chipsetType);
gVBoxAPI.UIVirtualBox.GetSystemProperties(data->vboxObj, &systemProperties);
if (systemProperties) {
gVBoxAPI.UISystemProperties.GetMaxNetworkAdapters(systemProperties, chipsetType,
&networkAdapterCount);
VBOX_RELEASE(systemProperties);
}
VIR_DEBUG("Number of Network Cards to be connected: %zu", def->nnets);
VIR_DEBUG("Number of Network Cards available: %d", networkAdapterCount);
for (i = 0; (i < def->nnets) && (i < networkAdapterCount); i++) {
INetworkAdapter *adapter = NULL;
PRUint32 adapterType = NetworkAdapterType_Null;
char macaddr[VIR_MAC_STRING_BUFLEN] = {0};
char macaddrvbox[VIR_MAC_STRING_BUFLEN - 5] = {0};
PRUnichar *MACAddress = NULL;
virMacAddrFormat(&def->nets[i]->mac, macaddr);
snprintf(macaddrvbox, VIR_MAC_STRING_BUFLEN - 5,
"%02X%02X%02X%02X%02X%02X",
def->nets[i]->mac.addr[0],
def->nets[i]->mac.addr[1],
def->nets[i]->mac.addr[2],
def->nets[i]->mac.addr[3],
def->nets[i]->mac.addr[4],
def->nets[i]->mac.addr[5]);
macaddrvbox[VIR_MAC_STRING_BUFLEN - 6] = '\0';
VIR_DEBUG("NIC(%zu): Type: %d", i, def->nets[i]->type);
VIR_DEBUG("NIC(%zu): Model: %s", i, def->nets[i]->model);
VIR_DEBUG("NIC(%zu): Mac: %s", i, macaddr);
VIR_DEBUG("NIC(%zu): ifname: %s", i, def->nets[i]->ifname);
if (def->nets[i]->type == VIR_DOMAIN_NET_TYPE_NETWORK) {
VIR_DEBUG("NIC(%zu): name: %s", i, def->nets[i]->data.network.name);
} else if (def->nets[i]->type == VIR_DOMAIN_NET_TYPE_INTERNAL) {
VIR_DEBUG("NIC(%zu): name: %s", i, def->nets[i]->data.internal.name);
} else if (def->nets[i]->type == VIR_DOMAIN_NET_TYPE_USER) {
VIR_DEBUG("NIC(%zu): NAT.", i);
} else if (def->nets[i]->type == VIR_DOMAIN_NET_TYPE_BRIDGE) {
VIR_DEBUG("NIC(%zu): brname: %s", i, def->nets[i]->data.bridge.brname);
VIR_DEBUG("NIC(%zu): script: %s", i, def->nets[i]->script);
VIR_DEBUG("NIC(%zu): ipaddr: %s", i, def->nets[i]->data.bridge.ipaddr);
}
gVBoxAPI.UIMachine.GetNetworkAdapter(machine, i, &adapter);
if (!adapter)
continue;
gVBoxAPI.UINetworkAdapter.SetEnabled(adapter, 1);
if (def->nets[i]->model) {
if (STRCASEEQ(def->nets[i]->model, "Am79C970A")) {
adapterType = NetworkAdapterType_Am79C970A;
} else if (STRCASEEQ(def->nets[i]->model, "Am79C973")) {
adapterType = NetworkAdapterType_Am79C973;
} else if (STRCASEEQ(def->nets[i]->model, "82540EM")) {
adapterType = NetworkAdapterType_I82540EM;
} else if (STRCASEEQ(def->nets[i]->model, "82545EM")) {
adapterType = NetworkAdapterType_I82545EM;
} else if (STRCASEEQ(def->nets[i]->model, "82543GC")) {
adapterType = NetworkAdapterType_I82543GC;
} else if (gVBoxAPI.APIVersion >= 3000051 &&
STRCASEEQ(def->nets[i]->model, "virtio")) {
/* Only vbox 3.1 and later support NetworkAdapterType_Virto */
adapterType = NetworkAdapterType_Virtio;
}
} else {
adapterType = NetworkAdapterType_Am79C973;
}
gVBoxAPI.UINetworkAdapter.SetAdapterType(adapter, adapterType);
if (def->nets[i]->type == VIR_DOMAIN_NET_TYPE_BRIDGE) {
PRUnichar *hostInterface = NULL;
/* Bridged Network */
gVBoxAPI.UINetworkAdapter.AttachToBridgedInterface(adapter);
if (def->nets[i]->data.bridge.brname) {
VBOX_UTF8_TO_UTF16(def->nets[i]->data.bridge.brname,
&hostInterface);
gVBoxAPI.UINetworkAdapter.SetBridgedInterface(adapter, hostInterface);
VBOX_UTF16_FREE(hostInterface);
}
} else if (def->nets[i]->type == VIR_DOMAIN_NET_TYPE_INTERNAL) {
PRUnichar *internalNetwork = NULL;
/* Internal Network */
gVBoxAPI.UINetworkAdapter.AttachToInternalNetwork(adapter);
if (def->nets[i]->data.internal.name) {
VBOX_UTF8_TO_UTF16(def->nets[i]->data.internal.name,
&internalNetwork);
gVBoxAPI.UINetworkAdapter.SetInternalNetwork(adapter, internalNetwork);
VBOX_UTF16_FREE(internalNetwork);
}
} else if (def->nets[i]->type == VIR_DOMAIN_NET_TYPE_NETWORK) {
PRUnichar *hostInterface = NULL;
/* Host Only Networking (currently only vboxnet0 available
* on *nix and mac, on windows you can create and configure
* as many as you want)
*/
gVBoxAPI.UINetworkAdapter.AttachToHostOnlyInterface(adapter);
if (def->nets[i]->data.network.name) {
VBOX_UTF8_TO_UTF16(def->nets[i]->data.network.name,
&hostInterface);
gVBoxAPI.UINetworkAdapter.SetHostOnlyInterface(adapter, hostInterface);
VBOX_UTF16_FREE(hostInterface);
}
} else if (def->nets[i]->type == VIR_DOMAIN_NET_TYPE_USER) {
/* NAT */
gVBoxAPI.UINetworkAdapter.AttachToNAT(adapter);
} else {
/* else always default to NAT if we don't understand
* what option is been passed to us
*/
gVBoxAPI.UINetworkAdapter.AttachToNAT(adapter);
}
VBOX_UTF8_TO_UTF16(macaddrvbox, &MACAddress);
gVBoxAPI.UINetworkAdapter.SetMACAddress(adapter, MACAddress);
VBOX_UTF16_FREE(MACAddress);
}
}
static void
vboxAttachSerial(virDomainDefPtr def, vboxGlobalData *data, IMachine *machine)
{
ISystemProperties *systemProperties = NULL;
PRUint32 serialPortCount = 0;
size_t i = 0;
gVBoxAPI.UIVirtualBox.GetSystemProperties(data->vboxObj, &systemProperties);
if (systemProperties) {
gVBoxAPI.UISystemProperties.GetSerialPortCount(systemProperties,
&serialPortCount);
VBOX_RELEASE(systemProperties);
}
VIR_DEBUG("Number of Serial Ports to be connected: %zu", def->nserials);
VIR_DEBUG("Number of Serial Ports available: %d", serialPortCount);
for (i = 0; (i < def->nserials) && (i < serialPortCount); i++) {
ISerialPort *serialPort = NULL;
PRUnichar *pathUtf16 = NULL;
VIR_DEBUG("SerialPort(%zu): Type: %d", i, def->serials[i]->source.type);
VIR_DEBUG("SerialPort(%zu): target.port: %d", i,
def->serials[i]->target.port);
gVBoxAPI.UIMachine.GetSerialPort(machine, i, &serialPort);
if (!serialPort)
continue;
gVBoxAPI.UISerialPort.SetEnabled(serialPort, 1);
if (def->serials[i]->source.data.file.path) {
VBOX_UTF8_TO_UTF16(def->serials[i]->source.data.file.path,
&pathUtf16);
gVBoxAPI.UISerialPort.SetPath(serialPort, pathUtf16);
}
/* For now hard code the serial ports to COM1 and COM2,
* COM1 (Base Addr: 0x3F8 (decimal: 1016), IRQ: 4)
* COM2 (Base Addr: 0x2F8 (decimal: 760), IRQ: 3)
* TODO: make this more flexible
*/
/* TODO: to improve the libvirt XMl handling so
* that def->serials[i]->target.port shows real port
* and not always start at 0
*/
if (def->serials[i]->target.port == 0) {
gVBoxAPI.UISerialPort.SetIRQ(serialPort, 4);
gVBoxAPI.UISerialPort.SetIOBase(serialPort, 1016);
VIR_DEBUG(" serialPort-%zu irq: %d, iobase 0x%x, path: %s",
i, 4, 1016, def->serials[i]->source.data.file.path);
} else if (def->serials[i]->target.port == 1) {
gVBoxAPI.UISerialPort.SetIRQ(serialPort, 3);
gVBoxAPI.UISerialPort.SetIOBase(serialPort, 760);
VIR_DEBUG(" serialPort-%zu irq: %d, iobase 0x%x, path: %s",
i, 3, 760, def->serials[i]->source.data.file.path);
}
if (def->serials[i]->source.type == VIR_DOMAIN_CHR_TYPE_DEV) {
gVBoxAPI.UISerialPort.SetHostMode(serialPort, PortMode_HostDevice);
} else if (def->serials[i]->source.type == VIR_DOMAIN_CHR_TYPE_PIPE) {
gVBoxAPI.UISerialPort.SetHostMode(serialPort, PortMode_HostPipe);
} else if (gVBoxAPI.APIVersion >= 2002051 &&
def->serials[i]->source.type == VIR_DOMAIN_CHR_TYPE_FILE) {
/* PortMode RawFile is used for vbox 3.0 or later */
gVBoxAPI.UISerialPort.SetHostMode(serialPort, PortMode_RawFile);
} else {
gVBoxAPI.UISerialPort.SetHostMode(serialPort,
PortMode_Disconnected);
}
VBOX_RELEASE(serialPort);
VBOX_UTF16_FREE(pathUtf16);
}
}
static void
vboxAttachParallel(virDomainDefPtr def, vboxGlobalData *data, IMachine *machine)
{
ISystemProperties *systemProperties = NULL;
PRUint32 parallelPortCount = 0;
size_t i = 0;
gVBoxAPI.UIVirtualBox.GetSystemProperties(data->vboxObj, &systemProperties);
if (systemProperties) {
gVBoxAPI.UISystemProperties.GetParallelPortCount(systemProperties,
&parallelPortCount);
VBOX_RELEASE(systemProperties);
}
VIR_DEBUG("Number of Parallel Ports to be connected: %zu", def->nparallels);
VIR_DEBUG("Number of Parallel Ports available: %d", parallelPortCount);
for (i = 0; (i < def->nparallels) && (i < parallelPortCount); i++) {
IParallelPort *parallelPort = NULL;
PRUnichar *pathUtf16 = NULL;
VIR_DEBUG("ParallelPort(%zu): Type: %d", i, def->parallels[i]->source.type);
VIR_DEBUG("ParallelPort(%zu): target.port: %d", i,
def->parallels[i]->target.port);
gVBoxAPI.UIMachine.GetParallelPort(machine, i, &parallelPort);
if (!parallelPort)
continue;
VBOX_UTF8_TO_UTF16(def->parallels[i]->source.data.file.path, &pathUtf16);
/* For now hard code the parallel ports to
* LPT1 (Base Addr: 0x378 (decimal: 888), IRQ: 7)
* LPT2 (Base Addr: 0x278 (decimal: 632), IRQ: 5)
* TODO: make this more flexible
*/
if ((def->parallels[i]->source.type == VIR_DOMAIN_CHR_TYPE_DEV) ||
(def->parallels[i]->source.type == VIR_DOMAIN_CHR_TYPE_PTY) ||
(def->parallels[i]->source.type == VIR_DOMAIN_CHR_TYPE_FILE) ||
(def->parallels[i]->source.type == VIR_DOMAIN_CHR_TYPE_PIPE)) {
gVBoxAPI.UIParallelPort.SetPath(parallelPort, pathUtf16);
if (i == 0) {
gVBoxAPI.UIParallelPort.SetIRQ(parallelPort, 7);
gVBoxAPI.UIParallelPort.SetIOBase(parallelPort, 888);
VIR_DEBUG(" parallePort-%zu irq: %d, iobase 0x%x, path: %s",
i, 7, 888, def->parallels[i]->source.data.file.path);
} else if (i == 1) {
gVBoxAPI.UIParallelPort.SetIRQ(parallelPort, 5);
gVBoxAPI.UIParallelPort.SetIOBase(parallelPort, 632);
VIR_DEBUG(" parallePort-%zu irq: %d, iobase 0x%x, path: %s",
i, 5, 632, def->parallels[i]->source.data.file.path);
}
}
/* like serial port, parallel port can't be enabled unless
* correct IRQ and IOBase values are specified.
*/
gVBoxAPI.UIParallelPort.SetEnabled(parallelPort, 1);
VBOX_RELEASE(parallelPort);
VBOX_UTF16_FREE(pathUtf16);
}
}
static void
vboxAttachVideo(virDomainDefPtr def, IMachine *machine)
{
if ((def->nvideos == 1) &&
(def->videos[0]->type == VIR_DOMAIN_VIDEO_TYPE_VBOX)) {
gVBoxAPI.UIMachine.SetVRAMSize(machine,
VIR_DIV_UP(def->videos[0]->vram, 1024));
gVBoxAPI.UIMachine.SetMonitorCount(machine, def->videos[0]->heads);
if (def->videos[0]->accel) {
gVBoxAPI.UIMachine.SetAccelerate3DEnabled(machine,
def->videos[0]->accel->support3d);
if (gVBoxAPI.accelerate2DVideo)
gVBoxAPI.UIMachine.SetAccelerate2DVideoEnabled(machine,
def->videos[0]->accel->support2d);
} else {
gVBoxAPI.UIMachine.SetAccelerate3DEnabled(machine, 0);
if (gVBoxAPI.accelerate2DVideo)
gVBoxAPI.UIMachine.SetAccelerate2DVideoEnabled(machine, 0);
}
}
}
static void
vboxAttachDisplay(virDomainDefPtr def, vboxGlobalData *data, IMachine *machine)
{
int vrdpPresent = 0;
int sdlPresent = 0;
int guiPresent = 0;
char *guiDisplay = NULL;
char *sdlDisplay = NULL;
size_t i = 0;
for (i = 0; i < def->ngraphics; i++) {
IVRDxServer *VRDxServer = NULL;
if ((def->graphics[i]->type == VIR_DOMAIN_GRAPHICS_TYPE_RDP) &&
(vrdpPresent == 0)) {
vrdpPresent = 1;
gVBoxAPI.UIMachine.GetVRDxServer(machine, &VRDxServer);
if (VRDxServer) {
const char *listenAddr
= virDomainGraphicsListenGetAddress(def->graphics[i], 0);
gVBoxAPI.UIVRDxServer.SetEnabled(VRDxServer, PR_TRUE);
VIR_DEBUG("VRDP Support turned ON.");
gVBoxAPI.UIVRDxServer.SetPorts(data, VRDxServer, def->graphics[i]);
if (def->graphics[i]->data.rdp.replaceUser) {
gVBoxAPI.UIVRDxServer.SetReuseSingleConnection(VRDxServer,
PR_TRUE);
VIR_DEBUG("VRDP set to reuse single connection");
}
if (def->graphics[i]->data.rdp.multiUser) {
gVBoxAPI.UIVRDxServer.SetAllowMultiConnection(VRDxServer,
PR_TRUE);
VIR_DEBUG("VRDP set to allow multiple connection");
}
if (listenAddr) {
PRUnichar *netAddressUtf16 = NULL;
VBOX_UTF8_TO_UTF16(listenAddr, &netAddressUtf16);
gVBoxAPI.UIVRDxServer.SetNetAddress(data, VRDxServer,
netAddressUtf16);
VIR_DEBUG("VRDP listen address is set to: %s",
listenAddr);
VBOX_UTF16_FREE(netAddressUtf16);
}
VBOX_RELEASE(VRDxServer);
}
}
if ((def->graphics[i]->type == VIR_DOMAIN_GRAPHICS_TYPE_DESKTOP) &&
(guiPresent == 0)) {
guiPresent = 1;
if (VIR_STRDUP(guiDisplay, def->graphics[i]->data.desktop.display) < 0) {
/* just don't go to cleanup yet as it is ok to have
* guiDisplay as NULL and we check it below if it
* exist and then only use it there
*/
}
}
if ((def->graphics[i]->type == VIR_DOMAIN_GRAPHICS_TYPE_SDL) &&
(sdlPresent == 0)) {
sdlPresent = 1;
if (VIR_STRDUP(sdlDisplay, def->graphics[i]->data.sdl.display) < 0) {
/* just don't go to cleanup yet as it is ok to have
* sdlDisplay as NULL and we check it below if it
* exist and then only use it there
*/
}
}
}
if ((vrdpPresent == 1) && (guiPresent == 0) && (sdlPresent == 0)) {
/* store extradata key that frontend is set to vrdp */
PRUnichar *keyTypeUtf16 = NULL;
PRUnichar *valueTypeUtf16 = NULL;
VBOX_UTF8_TO_UTF16("FRONTEND/Type", &keyTypeUtf16);
VBOX_UTF8_TO_UTF16("vrdp", &valueTypeUtf16);
gVBoxAPI.UIMachine.SetExtraData(machine, keyTypeUtf16, valueTypeUtf16);
VBOX_UTF16_FREE(keyTypeUtf16);
VBOX_UTF16_FREE(valueTypeUtf16);
} else if ((guiPresent == 0) && (sdlPresent == 1)) {
/* store extradata key that frontend is set to sdl */
PRUnichar *keyTypeUtf16 = NULL;
PRUnichar *valueTypeUtf16 = NULL;
PRUnichar *keyDislpayUtf16 = NULL;
PRUnichar *valueDisplayUtf16 = NULL;
VBOX_UTF8_TO_UTF16("FRONTEND/Type", &keyTypeUtf16);
VBOX_UTF8_TO_UTF16("sdl", &valueTypeUtf16);
gVBoxAPI.UIMachine.SetExtraData(machine, keyTypeUtf16, valueTypeUtf16);
VBOX_UTF16_FREE(keyTypeUtf16);
VBOX_UTF16_FREE(valueTypeUtf16);
if (sdlDisplay) {
VBOX_UTF8_TO_UTF16("FRONTEND/Display", &keyDislpayUtf16);
VBOX_UTF8_TO_UTF16(sdlDisplay, &valueDisplayUtf16);
gVBoxAPI.UIMachine.SetExtraData(machine, keyDislpayUtf16,
valueDisplayUtf16);
VBOX_UTF16_FREE(keyDislpayUtf16);
VBOX_UTF16_FREE(valueDisplayUtf16);
}
} else {
/* if all are set then default is gui, with vrdp turned on */
PRUnichar *keyTypeUtf16 = NULL;
PRUnichar *valueTypeUtf16 = NULL;
PRUnichar *keyDislpayUtf16 = NULL;
PRUnichar *valueDisplayUtf16 = NULL;
VBOX_UTF8_TO_UTF16("FRONTEND/Type", &keyTypeUtf16);
VBOX_UTF8_TO_UTF16("gui", &valueTypeUtf16);
gVBoxAPI.UIMachine.SetExtraData(machine, keyTypeUtf16, valueTypeUtf16);
VBOX_UTF16_FREE(keyTypeUtf16);
VBOX_UTF16_FREE(valueTypeUtf16);
if (guiDisplay) {
VBOX_UTF8_TO_UTF16("FRONTEND/Display", &keyDislpayUtf16);
VBOX_UTF8_TO_UTF16(guiDisplay, &valueDisplayUtf16);
gVBoxAPI.UIMachine.SetExtraData(machine, keyDislpayUtf16,
valueDisplayUtf16);
VBOX_UTF16_FREE(keyDislpayUtf16);
VBOX_UTF16_FREE(valueDisplayUtf16);
}
}
VIR_FREE(guiDisplay);
VIR_FREE(sdlDisplay);
}
static void
vboxAttachUSB(virDomainDefPtr def, vboxGlobalData *data, IMachine *machine)
{
IUSBCommon *USBCommon = NULL;
size_t i = 0;
bool isUSB = false;
nsresult rc;
if (def->nhostdevs == 0)
return;
/* Loop through the devices first and see if you
* have a USB Device, only if you have one then
* start the USB controller else just proceed as
* usual
*/
for (i = 0; i < def->nhostdevs; i++) {
if (def->hostdevs[i]->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS)
continue;
if (def->hostdevs[i]->source.subsys.type !=
VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB)
continue;
if (!def->hostdevs[i]->source.subsys.u.usb.vendor &&
!def->hostdevs[i]->source.subsys.u.usb.product)
continue;
VIR_DEBUG("USB Device detected, VendorId:0x%x, ProductId:0x%x",
def->hostdevs[i]->source.subsys.u.usb.vendor,
def->hostdevs[i]->source.subsys.u.usb.product);
isUSB = true;
break;
}
if (!isUSB)
return;
/* First Start the USB Controller and then loop
* to attach USB Devices to it
*/
rc = gVBoxAPI.UIMachine.GetUSBCommon(machine, &USBCommon);
if (NS_FAILED(rc) || !USBCommon)
return;
gVBoxAPI.UIUSBCommon.Enable(USBCommon);
for (i = 0; i < def->nhostdevs; i++) {
char *filtername = NULL;
PRUnichar *filternameUtf16 = NULL;
IUSBDeviceFilter *filter = NULL;
PRUnichar *vendorIdUtf16 = NULL;
char vendorId[40] = {0};
PRUnichar *productIdUtf16 = NULL;
char productId[40] = {0};
if (def->hostdevs[i]->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS)
continue;
if (def->hostdevs[i]->source.subsys.type !=
VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB)
continue;
/* Zero pad for nice alignment when fewer than 9999
* devices.
*/
if (virAsprintf(&filtername, "filter%04zu", i) >= 0) {
VBOX_UTF8_TO_UTF16(filtername, &filternameUtf16);
VIR_FREE(filtername);
gVBoxAPI.UIUSBCommon.CreateDeviceFilter(USBCommon,
filternameUtf16,
&filter);
}
VBOX_UTF16_FREE(filternameUtf16);
if (!filter)
continue;
if (!def->hostdevs[i]->source.subsys.u.usb.vendor &&
!def->hostdevs[i]->source.subsys.u.usb.product)
continue;
if (def->hostdevs[i]->source.subsys.u.usb.vendor) {
snprintf(vendorId, sizeof(vendorId), "%x",
def->hostdevs[i]->source.subsys.u.usb.vendor);
VBOX_UTF8_TO_UTF16(vendorId, &vendorIdUtf16);
gVBoxAPI.UIUSBDeviceFilter.SetVendorId(filter, vendorIdUtf16);
VBOX_UTF16_FREE(vendorIdUtf16);
}
if (def->hostdevs[i]->source.subsys.u.usb.product) {
snprintf(productId, sizeof(productId), "%x",
def->hostdevs[i]->source.subsys.u.usb.product);
VBOX_UTF8_TO_UTF16(productId, &productIdUtf16);
gVBoxAPI.UIUSBDeviceFilter.SetProductId(filter,
productIdUtf16);
VBOX_UTF16_FREE(productIdUtf16);
}
gVBoxAPI.UIUSBDeviceFilter.SetActive(filter, 1);
gVBoxAPI.UIUSBCommon.InsertDeviceFilter(USBCommon, i, filter);
VBOX_RELEASE(filter);
}
VBOX_RELEASE(USBCommon);
}
static void
vboxAttachSharedFolder(virDomainDefPtr def, vboxGlobalData *data, IMachine *machine)
{
size_t i;
PRUnichar *nameUtf16;
PRUnichar *hostPathUtf16;
PRBool writable;
if (def->nfss == 0)
return;
for (i = 0; i < def->nfss; i++) {
if (def->fss[i]->type != VIR_DOMAIN_FS_TYPE_MOUNT)
continue;
VBOX_UTF8_TO_UTF16(def->fss[i]->dst, &nameUtf16);
VBOX_UTF8_TO_UTF16(def->fss[i]->src, &hostPathUtf16);
writable = !def->fss[i]->readonly;
gVBoxAPI.UIMachine.CreateSharedFolder(machine, nameUtf16, hostPathUtf16,
writable, PR_FALSE);
VBOX_UTF16_FREE(nameUtf16);
VBOX_UTF16_FREE(hostPathUtf16);
}
}
static virDomainPtr vboxDomainDefineXML(virConnectPtr conn, const char *xml)
{
VBOX_OBJECT_CHECK(conn, virDomainPtr, NULL);
IMachine *machine = NULL;
IBIOSSettings *bios = NULL;
vboxIIDUnion mchiid;
virDomainDefPtr def = NULL;
nsresult rc;
char uuidstr[VIR_UUID_STRING_BUFLEN];
VBOX_IID_INITIALIZE(&mchiid);
if (!(def = virDomainDefParseString(xml, data->caps, data->xmlopt,
1 << VIR_DOMAIN_VIRT_VBOX,
VIR_DOMAIN_XML_INACTIVE))) {
goto cleanup;
}
virUUIDFormat(def->uuid, uuidstr);
rc = gVBoxAPI.UIVirtualBox.CreateMachine(data, def, &machine, uuidstr);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("could not define a domain, rc=%08x"), (unsigned)rc);
goto cleanup;
}
rc = gVBoxAPI.UIMachine.SetMemorySize(machine,
VIR_DIV_UP(def->mem.cur_balloon, 1024));
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("could not set the memory size of the domain to: %llu Kb, "
"rc=%08x"),
def->mem.cur_balloon, (unsigned)rc);
}
if (def->vcpus != def->maxvcpus) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("current vcpu count must equal maximum"));
}
rc = gVBoxAPI.UIMachine.SetCPUCount(machine, def->maxvcpus);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("could not set the number of virtual CPUs to: %u, rc=%08x"),
def->maxvcpus, (unsigned)rc);
}
rc = gVBoxAPI.UIMachine.SetCPUProperty(machine, CPUPropertyType_PAE,
def->features[VIR_DOMAIN_FEATURE_PAE] ==
VIR_TRISTATE_SWITCH_ON);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("could not change PAE status to: %s, rc=%08x"),
(def->features[VIR_DOMAIN_FEATURE_PAE] == VIR_TRISTATE_SWITCH_ON)
? _("Enabled") : _("Disabled"), (unsigned)rc);
}
gVBoxAPI.UIMachine.GetBIOSSettings(machine, &bios);
if (bios) {
rc = gVBoxAPI.UIBIOSSettings.SetACPIEnabled(bios,
def->features[VIR_DOMAIN_FEATURE_ACPI] ==
VIR_TRISTATE_SWITCH_ON);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("could not change ACPI status to: %s, rc=%08x"),
(def->features[VIR_DOMAIN_FEATURE_ACPI] == VIR_TRISTATE_SWITCH_ON)
? _("Enabled") : _("Disabled"), (unsigned)rc);
}
rc = gVBoxAPI.UIBIOSSettings.SetIOAPICEnabled(bios,
def->features[VIR_DOMAIN_FEATURE_APIC] ==
VIR_TRISTATE_SWITCH_ON);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("could not change APIC status to: %s, rc=%08x"),
(def->features[VIR_DOMAIN_FEATURE_APIC] == VIR_TRISTATE_SWITCH_ON)
? _("Enabled") : _("Disabled"), (unsigned)rc);
}
VBOX_RELEASE(bios);
}
/* Register the machine before attaching other devices to it */
rc = gVBoxAPI.UIVirtualBox.RegisterMachine(data->vboxObj, machine);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("could not define a domain, rc=%08x"), (unsigned)rc);
goto cleanup;
}
/* Get the uuid of the machine, currently it is immutable
* object so open a session to it and get it back, so that
* you can make changes to the machine setting
*/
gVBoxAPI.UIMachine.GetId(machine, &mchiid);
gVBoxAPI.UISession.Open(data, &mchiid, machine);
gVBoxAPI.UISession.GetMachine(data->vboxSession, &machine);
vboxSetBootDeviceOrder(def, data, machine);
vboxAttachDrives(def, data, machine);
vboxAttachSound(def, machine);
vboxAttachNetwork(def, data, machine);
vboxAttachSerial(def, data, machine);
vboxAttachParallel(def, data, machine);
vboxAttachVideo(def, machine);
vboxAttachDisplay(def, data, machine);
vboxAttachUSB(def, data, machine);
vboxAttachSharedFolder(def, data, machine);
/* Save the machine settings made till now and close the
* session. also free up the mchiid variable used.
*/
rc = gVBoxAPI.UIMachine.SaveSettings(machine);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("failed no saving settings, rc=%08x"), (unsigned)rc);
goto cleanup;
}
gVBoxAPI.UISession.Close(data->vboxSession);
vboxIIDUnalloc(&mchiid);
ret = virGetDomain(conn, def->name, def->uuid);
VBOX_RELEASE(machine);
virDomainDefFree(def);
return ret;
cleanup:
VBOX_RELEASE(machine);
virDomainDefFree(def);
return NULL;
}
static void
detachDevices_common(vboxGlobalData *data, vboxIIDUnion *iidu)
{
/* Block for checking if HDD's are attched to VM.
* considering just IDE bus for now. Also skipped
* chanel=1 and device=0 (Secondary Master) as currenlty
* it is allocated to CD/DVD Drive by default.
*
* Only do this for VirtualBox 3.x and before. Since
* VirtualBox 4.0 the Unregister method can do this for use.
*/
IMachine *machine = NULL;
PRUnichar *hddcnameUtf16 = NULL;
nsresult rc;
char *hddcname;
if (!gVBoxAPI.detachDevicesExplicitly)
VIR_WARN("This function may not work in current vbox version");
ignore_value(VIR_STRDUP(hddcname, "IDE"));
VBOX_UTF8_TO_UTF16(hddcname, &hddcnameUtf16);
VIR_FREE(hddcname);
/* Open a Session for the machine */
rc = gVBoxAPI.UISession.Open(data, iidu, machine);
if (NS_SUCCEEDED(rc)) {
rc = gVBoxAPI.UISession.GetMachine(data->vboxSession, &machine);
if (NS_SUCCEEDED(rc) && machine) {
gVBoxAPI.detachDevices(data, machine, hddcnameUtf16);
gVBoxAPI.UIMachine.SaveSettings(machine);
}
gVBoxAPI.UISession.Close(data->vboxSession);
}
VBOX_UTF16_FREE(hddcnameUtf16);
}
static int vboxDomainUndefineFlags(virDomainPtr dom, unsigned int flags)
{
VBOX_OBJECT_CHECK(dom->conn, int, -1);
IMachine *machine = NULL;
vboxIIDUnion iid;
nsresult rc;
gVBoxAPI.UIID.vboxIIDInitialize(&iid);
/* No managed save, so we explicitly reject
* VIR_DOMAIN_UNDEFINE_MANAGED_SAVE. No snapshot metadata for
* VBox, so we can trivially ignore that flag. */
virCheckFlags(VIR_DOMAIN_UNDEFINE_SNAPSHOTS_METADATA, -1);
vboxIIDFromUUID(&iid, dom->uuid);
if (gVBoxAPI.detachDevicesExplicitly)
detachDevices_common(data, &iid);
rc = gVBoxAPI.unregisterMachine(data, &iid, &machine);
DEBUGIID("UUID of machine being undefined", &iid);
if (NS_SUCCEEDED(rc)) {
gVBoxAPI.deleteConfig(machine);
ret = 0;
} else {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("could not delete the domain, rc=%08x"), (unsigned)rc);
}
vboxIIDUnalloc(&iid);
VBOX_RELEASE(machine);
return ret;
}
static int vboxDomainUndefine(virDomainPtr dom)
{
return vboxDomainUndefineFlags(dom, 0);
}
static int
vboxStartMachine(virDomainPtr dom, int maxDomID, IMachine *machine, vboxIIDUnion *iid)
{
VBOX_OBJECT_CHECK(dom->conn, int, -1);
int vrdpPresent = 0;
int sdlPresent = 0;
int guiPresent = 0;
char *guiDisplay = NULL;
char *sdlDisplay = NULL;
PRUnichar *keyTypeUtf16 = NULL;
PRUnichar *valueTypeUtf16 = NULL;
char *valueTypeUtf8 = NULL;
PRUnichar *keyDislpayUtf16 = NULL;
PRUnichar *valueDisplayUtf16 = NULL;
char *valueDisplayUtf8 = NULL;
IProgress *progress = NULL;
PRUnichar *env = NULL;
PRUnichar *sessionType = NULL;
nsresult rc;
VBOX_UTF8_TO_UTF16("FRONTEND/Type", &keyTypeUtf16);
gVBoxAPI.UIMachine.GetExtraData(machine, keyTypeUtf16, &valueTypeUtf16);
VBOX_UTF16_FREE(keyTypeUtf16);
if (valueTypeUtf16) {
VBOX_UTF16_TO_UTF8(valueTypeUtf16, &valueTypeUtf8);
VBOX_UTF16_FREE(valueTypeUtf16);
if (STREQ(valueTypeUtf8, "sdl") || STREQ(valueTypeUtf8, "gui")) {
VBOX_UTF8_TO_UTF16("FRONTEND/Display", &keyDislpayUtf16);
gVBoxAPI.UIMachine.GetExtraData(machine, keyDislpayUtf16,
&valueDisplayUtf16);
VBOX_UTF16_FREE(keyDislpayUtf16);
if (valueDisplayUtf16) {
VBOX_UTF16_TO_UTF8(valueDisplayUtf16, &valueDisplayUtf8);
VBOX_UTF16_FREE(valueDisplayUtf16);
if (strlen(valueDisplayUtf8) <= 0)
VBOX_UTF8_FREE(valueDisplayUtf8);
}
if (STREQ(valueTypeUtf8, "sdl")) {
sdlPresent = 1;
if (VIR_STRDUP(sdlDisplay, valueDisplayUtf8) < 0) {
/* just don't go to cleanup yet as it is ok to have
* sdlDisplay as NULL and we check it below if it
* exist and then only use it there
*/
}
}
if (STREQ(valueTypeUtf8, "gui")) {
guiPresent = 1;
if (VIR_STRDUP(guiDisplay, valueDisplayUtf8) < 0) {
/* just don't go to cleanup yet as it is ok to have
* guiDisplay as NULL and we check it below if it
* exist and then only use it there
*/
}
}
}
if (STREQ(valueTypeUtf8, "vrdp")) {
vrdpPresent = 1;
}
if (!vrdpPresent && !sdlPresent && !guiPresent) {
/* if nothing is selected it means either the machine xml
* file is really old or some values are missing so fallback
*/
guiPresent = 1;
}
VBOX_UTF8_FREE(valueTypeUtf8);
} else {
guiPresent = 1;
}
VBOX_UTF8_FREE(valueDisplayUtf8);
if (guiPresent) {
if (guiDisplay) {
char *displayutf8;
if (virAsprintf(&displayutf8, "DISPLAY=%s", guiDisplay) >= 0) {
VBOX_UTF8_TO_UTF16(displayutf8, &env);
VIR_FREE(displayutf8);
}
VIR_FREE(guiDisplay);
}
VBOX_UTF8_TO_UTF16("gui", &sessionType);
}
if (sdlPresent) {
if (sdlDisplay) {
char *displayutf8;
if (virAsprintf(&displayutf8, "DISPLAY=%s", sdlDisplay) >= 0) {
VBOX_UTF8_TO_UTF16(displayutf8, &env);
VIR_FREE(displayutf8);
}
VIR_FREE(sdlDisplay);
}
VBOX_UTF8_TO_UTF16("sdl", &sessionType);
}
if (vrdpPresent) {
VBOX_UTF8_TO_UTF16("vrdp", &sessionType);
}
rc = gVBoxAPI.UIMachine.LaunchVMProcess(data, machine, iid,
sessionType, env,
&progress);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_OPERATION_FAILED, "%s",
_("OpenRemoteSession/LaunchVMProcess failed, domain can't be started"));
ret = -1;
} else {
PRBool completed = 0;
resultCodeUnion resultCode;
gVBoxAPI.UIProgress.WaitForCompletion(progress, -1);
rc = gVBoxAPI.UIProgress.GetCompleted(progress, &completed);
if (NS_FAILED(rc)) {
/* error */
ret = -1;
}
gVBoxAPI.UIProgress.GetResultCode(progress, &resultCode);
if (RC_FAILED(resultCode)) {
/* error */
ret = -1;
} else {
/* all ok set the domid */
dom->id = maxDomID + 1;
ret = 0;
}
}
VBOX_RELEASE(progress);
gVBoxAPI.UISession.Close(data->vboxSession);
VBOX_UTF16_FREE(env);
VBOX_UTF16_FREE(sessionType);
return ret;
}
static int vboxDomainCreateWithFlags(virDomainPtr dom, unsigned int flags)
{
VBOX_OBJECT_CHECK(dom->conn, int, -1);
vboxArray machines = VBOX_ARRAY_INITIALIZER;
unsigned char uuid[VIR_UUID_BUFLEN] = {0};
nsresult rc;
size_t i = 0;
virCheckFlags(0, -1);
if (!dom->name) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Error while reading the domain name"));
goto cleanup;
}
rc = gVBoxAPI.UArray.vboxArrayGet(&machines, data->vboxObj, ARRAY_GET_MACHINES);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Could not get list of machines, rc=%08x"), (unsigned)rc);
goto cleanup;
}
for (i = 0; i < machines.count; ++i) {
IMachine *machine = machines.items[i];
PRBool isAccessible = PR_FALSE;
if (!machine)
continue;
gVBoxAPI.UIMachine.GetAccessible(machine, &isAccessible);
if (isAccessible) {
vboxIIDUnion iid;
VBOX_IID_INITIALIZE(&iid);
rc = gVBoxAPI.UIMachine.GetId(machine, &iid);
if (NS_FAILED(rc))
continue;
vboxIIDToUUID(&iid, uuid);
if (memcmp(dom->uuid, uuid, VIR_UUID_BUFLEN) == 0) {
PRUint32 state;
gVBoxAPI.UIMachine.GetState(machine, &state);
if (gVBoxAPI.machineStateChecker.NotStart(state)) {
ret = vboxStartMachine(dom, i, machine, &iid);
} else {
virReportError(VIR_ERR_OPERATION_FAILED, "%s",
_("machine is not in "
"poweroff|saved|aborted state, so "
"couldn't start it"));
ret = -1;
}
}
vboxIIDUnalloc(&iid);
if (ret != -1)
break;
}
}
/* Do the cleanup and take care you dont leak any memory */
gVBoxAPI.UArray.vboxArrayRelease(&machines);
cleanup:
return ret;
}
static int vboxDomainCreate(virDomainPtr dom)
{
return vboxDomainCreateWithFlags(dom, 0);
}
static virDomainPtr vboxDomainCreateXML(virConnectPtr conn, const char *xml,
unsigned int flags)
{
/* VirtualBox currently doesn't have support for running
* virtual machines without actually defining them and thus
* for time being just define new machine and start it.
*
* TODO: After the appropriate API's are added in VirtualBox
* change this behaviour to the expected one.
*/
virDomainPtr dom;
virCheckFlags(0, NULL);
dom = vboxDomainDefineXML(conn, xml);
if (dom == NULL)
return NULL;
if (vboxDomainCreate(dom) < 0) {
vboxDomainUndefineFlags(dom, 0);
virObjectUnref(dom);
return NULL;
}
return dom;
}
static int vboxDomainIsActive(virDomainPtr dom)
{
VBOX_OBJECT_CHECK(dom->conn, int, -1);
vboxArray machines = VBOX_ARRAY_INITIALIZER;
vboxIIDUnion iid;
char *machineNameUtf8 = NULL;
PRUnichar *machineNameUtf16 = NULL;
unsigned char uuid[VIR_UUID_BUFLEN];
size_t i;
bool matched = false;
nsresult rc;
VBOX_IID_INITIALIZE(&iid);
rc = gVBoxAPI.UArray.vboxArrayGet(&machines, data->vboxObj, ARRAY_GET_MACHINES);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Could not get list of machines, rc=%08x"), (unsigned)rc);
return ret;
}
for (i = 0; i < machines.count; ++i) {
IMachine *machine = machines.items[i];
PRBool isAccessible = PR_FALSE;
if (!machine)
continue;
gVBoxAPI.UIMachine.GetAccessible(machine, &isAccessible);
if (!isAccessible)
continue;
gVBoxAPI.UIMachine.GetId(machine, &iid);
if (NS_FAILED(rc))
continue;
vboxIIDToUUID(&iid, uuid);
vboxIIDUnalloc(&iid);
if (memcmp(dom->uuid, uuid, VIR_UUID_BUFLEN) == 0) {
PRUint32 state;
matched = true;
gVBoxAPI.UIMachine.GetName(machine, &machineNameUtf16);
VBOX_UTF16_TO_UTF8(machineNameUtf16, &machineNameUtf8);
gVBoxAPI.UIMachine.GetState(machine, &state);
if (gVBoxAPI.machineStateChecker.Online(state))
ret = 1;
else
ret = 0;
}
if (matched)
break;
}
/* Do the cleanup and take care you dont leak any memory */
VBOX_UTF8_FREE(machineNameUtf8);
VBOX_COM_UNALLOC_MEM(machineNameUtf16);
gVBoxAPI.UArray.vboxArrayRelease(&machines);
return ret;
}
static int vboxDomainIsPersistent(virDomainPtr dom)
{
/* All domains are persistent. However, we do want to check for
* existence. */
VBOX_OBJECT_CHECK(dom->conn, int, -1);
vboxIIDUnion iid;
IMachine *machine = NULL;
if (openSessionForMachine(data, dom->uuid, &iid, &machine, false) < 0)
goto cleanup;
ret = 1;
cleanup:
VBOX_RELEASE(machine);
vboxIIDUnalloc(&iid);
return ret;
}
static int vboxDomainIsUpdated(virDomainPtr dom)
{
/* VBox domains never have a persistent state that differs from
* current state. However, we do want to check for existence. */
VBOX_OBJECT_CHECK(dom->conn, int, -1);
vboxIIDUnion iid;
IMachine *machine = NULL;
if (openSessionForMachine(data, dom->uuid, &iid, &machine, false) < 0)
goto cleanup;
ret = 0;
cleanup:
VBOX_RELEASE(machine);
vboxIIDUnalloc(&iid);
return ret;
}
static int vboxDomainSuspend(virDomainPtr dom)
{
VBOX_OBJECT_CHECK(dom->conn, int, -1);
IMachine *machine = NULL;
vboxIIDUnion iid;
IConsole *console = NULL;
PRBool isAccessible = PR_FALSE;
PRUint32 state;
if (openSessionForMachine(data, dom->uuid, &iid, &machine, false) < 0)
goto cleanup;
if (!machine)
goto cleanup;
gVBoxAPI.UIMachine.GetAccessible(machine, &isAccessible);
if (!isAccessible)
goto cleanup;
gVBoxAPI.UIMachine.GetState(machine, &state);
if (gVBoxAPI.machineStateChecker.Running(state)) {
/* set state pause */
gVBoxAPI.UISession.OpenExisting(data, &iid, machine);
gVBoxAPI.UISession.GetConsole(data->vboxSession, &console);
if (console) {
gVBoxAPI.UIConsole.Pause(console);
VBOX_RELEASE(console);
ret = 0;
} else {
virReportError(VIR_ERR_OPERATION_FAILED, "%s",
_("error while suspending the domain"));
goto cleanup;
}
gVBoxAPI.UISession.Close(data->vboxSession);
} else {
virReportError(VIR_ERR_OPERATION_FAILED, "%s",
_("machine not in running state to suspend it"));
goto cleanup;
}
cleanup:
VBOX_RELEASE(machine);
vboxIIDUnalloc(&iid);
return ret;
}
static int vboxDomainResume(virDomainPtr dom)
{
VBOX_OBJECT_CHECK(dom->conn, int, -1);
IMachine *machine = NULL;
vboxIIDUnion iid;
IConsole *console = NULL;
PRUint32 state;
PRBool isAccessible = PR_FALSE;
if (openSessionForMachine(data, dom->uuid, &iid, &machine, false) < 0)
goto cleanup;
if (!machine)
goto cleanup;
gVBoxAPI.UIMachine.GetAccessible(machine, &isAccessible);
if (!isAccessible)
goto cleanup;
gVBoxAPI.UIMachine.GetState(machine, &state);
if (gVBoxAPI.machineStateChecker.Paused(state)) {
/* resume the machine here */
gVBoxAPI.UISession.OpenExisting(data, &iid, machine);
gVBoxAPI.UISession.GetConsole(data->vboxSession, &console);
if (console) {
gVBoxAPI.UIConsole.Resume(console);
VBOX_RELEASE(console);
ret = 0;
} else {
virReportError(VIR_ERR_OPERATION_FAILED, "%s",
_("error while resuming the domain"));
goto cleanup;
}
gVBoxAPI.UISession.Close(data->vboxSession);
} else {
virReportError(VIR_ERR_OPERATION_FAILED, "%s",
_("machine not paused, so can't resume it"));
goto cleanup;
}
cleanup:
VBOX_RELEASE(machine);
vboxIIDUnalloc(&iid);
return ret;
}
static int vboxDomainShutdownFlags(virDomainPtr dom, unsigned int flags)
{
VBOX_OBJECT_CHECK(dom->conn, int, -1);
IMachine *machine = NULL;
vboxIIDUnion iid;
IConsole *console = NULL;
PRUint32 state;
PRBool isAccessible = PR_FALSE;
virCheckFlags(0, -1);
if (openSessionForMachine(data, dom->uuid, &iid, &machine, false) < 0)
goto cleanup;
if (!machine)
goto cleanup;
gVBoxAPI.UIMachine.GetAccessible(machine, &isAccessible);
if (!isAccessible)
goto cleanup;
gVBoxAPI.UIMachine.GetState(machine, &state);
if (gVBoxAPI.machineStateChecker.Paused(state)) {
virReportError(VIR_ERR_OPERATION_FAILED, "%s",
_("machine paused, so can't power it down"));
goto cleanup;
} else if (gVBoxAPI.machineStateChecker.PoweredOff(state)) {
virReportError(VIR_ERR_OPERATION_FAILED, "%s",
_("machine already powered down"));
goto cleanup;
}
gVBoxAPI.UISession.OpenExisting(data, &iid, machine);
gVBoxAPI.UISession.GetConsole(data->vboxSession, &console);
if (console) {
gVBoxAPI.UIConsole.PowerButton(console);
VBOX_RELEASE(console);
ret = 0;
}
gVBoxAPI.UISession.Close(data->vboxSession);
cleanup:
VBOX_RELEASE(machine);
vboxIIDUnalloc(&iid);
return ret;
}
static int vboxDomainShutdown(virDomainPtr dom)
{
return vboxDomainShutdownFlags(dom, 0);
}
static int vboxDomainReboot(virDomainPtr dom, unsigned int flags)
{
VBOX_OBJECT_CHECK(dom->conn, int, -1);
IMachine *machine = NULL;
vboxIIDUnion iid;
IConsole *console = NULL;
PRUint32 state;
PRBool isAccessible = PR_FALSE;
virCheckFlags(0, -1);
if (openSessionForMachine(data, dom->uuid, &iid, &machine, false) < 0)
goto cleanup;
if (!machine)
goto cleanup;
gVBoxAPI.UIMachine.GetAccessible(machine, &isAccessible);
if (!isAccessible)
goto cleanup;
gVBoxAPI.UIMachine.GetState(machine, &state);
if (gVBoxAPI.machineStateChecker.Running(state)) {
gVBoxAPI.UISession.OpenExisting(data, &iid, machine);
gVBoxAPI.UISession.GetConsole(data->vboxSession, &console);
if (console) {
gVBoxAPI.UIConsole.Reset(console);
VBOX_RELEASE(console);
ret = 0;
}
gVBoxAPI.UISession.Close(data->vboxSession);
} else {
virReportError(VIR_ERR_OPERATION_FAILED, "%s",
_("machine not running, so can't reboot it"));
goto cleanup;
}
cleanup:
VBOX_RELEASE(machine);
vboxIIDUnalloc(&iid);
return ret;
}
static int vboxDomainDestroyFlags(virDomainPtr dom, unsigned int flags)
{
VBOX_OBJECT_CHECK(dom->conn, int, -1);
IMachine *machine = NULL;
vboxIIDUnion iid;
IConsole *console = NULL;
PRUint32 state;
PRBool isAccessible = PR_FALSE;
virCheckFlags(0, -1);
if (openSessionForMachine(data, dom->uuid, &iid, &machine, false) < 0)
goto cleanup;
if (!machine)
goto cleanup;
gVBoxAPI.UIMachine.GetAccessible(machine, &isAccessible);
if (!isAccessible)
goto cleanup;
gVBoxAPI.UIMachine.GetState(machine, &state);
if (gVBoxAPI.machineStateChecker.PoweredOff(state)) {
virReportError(VIR_ERR_OPERATION_FAILED, "%s",
_("machine already powered down"));
goto cleanup;
}
gVBoxAPI.UISession.OpenExisting(data, &iid, machine);
gVBoxAPI.UISession.GetConsole(data->vboxSession, &console);
if (console) {
gVBoxAPI.UIConsole.PowerDown(console);
VBOX_RELEASE(console);
dom->id = -1;
ret = 0;
}
gVBoxAPI.UISession.Close(data->vboxSession);
cleanup:
VBOX_RELEASE(machine);
vboxIIDUnalloc(&iid);
return ret;
}
static int vboxDomainDestroy(virDomainPtr dom)
{
return vboxDomainDestroyFlags(dom, 0);
}
static char *vboxDomainGetOSType(virDomainPtr dom ATTRIBUTE_UNUSED) {
/* Returning "hvm" always as suggested on list, cause
* this functions seems to be badly named and it
* is supposed to pass the ABI name and not the domain
* operating system driver as I had imagined ;)
*/
char *osType;
ignore_value(VIR_STRDUP(osType, "hvm"));
return osType;
}
static int vboxDomainSetMemory(virDomainPtr dom, unsigned long memory)
{
VBOX_OBJECT_CHECK(dom->conn, int, -1);
IMachine *machine = NULL;
vboxIIDUnion iid;
PRUint32 state;
PRBool isAccessible = PR_FALSE;
nsresult rc;
if (openSessionForMachine(data, dom->uuid, &iid, &machine, false) < 0)
goto cleanup;
if (!machine)
goto cleanup;
gVBoxAPI.UIMachine.GetAccessible(machine, &isAccessible);
if (!isAccessible)
goto cleanup;
gVBoxAPI.UIMachine.GetState(machine, &state);
if (!gVBoxAPI.machineStateChecker.PoweredOff(state)) {
virReportError(VIR_ERR_OPERATION_FAILED, "%s",
_("memory size can't be changed unless domain is powered down"));
goto cleanup;
}
rc = gVBoxAPI.UISession.Open(data, &iid, machine);
if (NS_FAILED(rc))
goto cleanup;
rc = gVBoxAPI.UISession.GetMachine(data->vboxSession, &machine);
if (NS_SUCCEEDED(rc) && machine) {
rc = gVBoxAPI.UIMachine.SetMemorySize(machine,
VIR_DIV_UP(memory, 1024));
if (NS_SUCCEEDED(rc)) {
gVBoxAPI.UIMachine.SaveSettings(machine);
ret = 0;
} else {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("could not set the memory size of the "
"domain to: %lu Kb, rc=%08x"),
memory, (unsigned)rc);
}
}
gVBoxAPI.UISession.Close(data->vboxSession);
cleanup:
VBOX_RELEASE(machine);
vboxIIDUnalloc(&iid);
return ret;
}
static int vboxDomainGetInfo(virDomainPtr dom, virDomainInfoPtr info)
{
VBOX_OBJECT_CHECK(dom->conn, int, -1);
vboxArray machines = VBOX_ARRAY_INITIALIZER;
char *machineName = NULL;
PRUnichar *machineNameUtf16 = NULL;
nsresult rc;
size_t i = 0;
rc = gVBoxAPI.UArray.vboxArrayGet(&machines, data->vboxObj, ARRAY_GET_MACHINES);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Could not get list of machines, rc=%08x"), (unsigned)rc);
goto cleanup;
}
info->nrVirtCpu = 0;
for (i = 0; i < machines.count; ++i) {
IMachine *machine = machines.items[i];
PRBool isAccessible = PR_FALSE;
if (!machine)
continue;
gVBoxAPI.UIMachine.GetAccessible(machine, &isAccessible);
if (!isAccessible)
continue;
gVBoxAPI.UIMachine.GetName(machine, &machineNameUtf16);
VBOX_UTF16_TO_UTF8(machineNameUtf16, &machineName);
if (STREQ(dom->name, machineName)) {
/* Get the Machine State (also match it with
* virDomainState). Get the Machine memory and
* for time being set max_balloon and cur_balloon to same
* Also since there is no direct way of checking
* the cputime required (one condition being the
* VM is remote), return zero for cputime. Get the
* number of CPU.
*/
PRUint32 CPUCount = 0;
PRUint32 memorySize = 0;
PRUint32 state;
PRUint32 maxMemorySize = 4 * 1024;
ISystemProperties *systemProperties = NULL;
gVBoxAPI.UIVirtualBox.GetSystemProperties(data->vboxObj, &systemProperties);
if (systemProperties) {
gVBoxAPI.UISystemProperties.GetMaxGuestRAM(systemProperties, &maxMemorySize);
VBOX_RELEASE(systemProperties);
systemProperties = NULL;
}
gVBoxAPI.UIMachine.GetCPUCount(machine, &CPUCount);
gVBoxAPI.UIMachine.GetMemorySize(machine, &memorySize);
gVBoxAPI.UIMachine.GetState(machine, &state);
info->cpuTime = 0;
info->nrVirtCpu = CPUCount;
info->memory = memorySize * 1024;
info->maxMem = maxMemorySize * 1024;
info->state = gVBoxAPI.vboxConvertState(state);
ret = 0;
}
VBOX_UTF8_FREE(machineName);
VBOX_COM_UNALLOC_MEM(machineNameUtf16);
if (info->nrVirtCpu)
break;
}
gVBoxAPI.UArray.vboxArrayRelease(&machines);
cleanup:
return ret;
}
static int vboxDomainGetState(virDomainPtr dom, int *state,
int *reason, unsigned int flags)
{
VBOX_OBJECT_CHECK(dom->conn, int, -1);
vboxIIDUnion domiid;
IMachine *machine = NULL;
PRUint32 mstate;
virCheckFlags(0, -1);
if (openSessionForMachine(data, dom->uuid, &domiid, &machine, false) < 0)
goto cleanup;
gVBoxAPI.UIMachine.GetState(machine, &mstate);
*state = gVBoxAPI.vboxConvertState(mstate);
if (reason)
*reason = 0;
ret = 0;
cleanup:
vboxIIDUnalloc(&domiid);
return ret;
}
static int vboxDomainSetVcpusFlags(virDomainPtr dom, unsigned int nvcpus,
unsigned int flags)
{
VBOX_OBJECT_CHECK(dom->conn, int, -1);
IMachine *machine = NULL;
vboxIIDUnion iid;
PRUint32 CPUCount = nvcpus;
nsresult rc;
if (flags != VIR_DOMAIN_AFFECT_LIVE) {
virReportError(VIR_ERR_INVALID_ARG, _("unsupported flags: (0x%x)"), flags);
return -1;
}
if (openSessionForMachine(data, dom->uuid, &iid, &machine, true) < 0)
return -1;
rc = gVBoxAPI.UISession.Open(data, &iid, machine);
if (NS_SUCCEEDED(rc)) {
gVBoxAPI.UISession.GetMachine(data->vboxSession, &machine);
if (machine) {
rc = gVBoxAPI.UIMachine.SetCPUCount(machine, CPUCount);
if (NS_SUCCEEDED(rc)) {
gVBoxAPI.UIMachine.SaveSettings(machine);
ret = 0;
} else {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("could not set the number of cpus of the domain "
"to: %u, rc=%08x"),
CPUCount, (unsigned)rc);
}
VBOX_RELEASE(machine);
} else {
virReportError(VIR_ERR_NO_DOMAIN,
_("no domain with matching id %d"), dom->id);
}
} else {
virReportError(VIR_ERR_NO_DOMAIN,
_("can't open session to the domain with id %d"), dom->id);
}
gVBoxAPI.UISession.Close(data->vboxSession);
vboxIIDUnalloc(&iid);
return ret;
}
static int vboxDomainSetVcpus(virDomainPtr dom, unsigned int nvcpus)
{
return vboxDomainSetVcpusFlags(dom, nvcpus, VIR_DOMAIN_AFFECT_LIVE);
}
static int vboxDomainGetVcpusFlags(virDomainPtr dom, unsigned int flags)
{
VBOX_OBJECT_CHECK(dom->conn, int, -1);
ISystemProperties *systemProperties = NULL;
PRUint32 maxCPUCount = 0;
if (flags != (VIR_DOMAIN_AFFECT_LIVE | VIR_DOMAIN_VCPU_MAXIMUM)) {
virReportError(VIR_ERR_INVALID_ARG, _("unsupported flags: (0x%x)"), flags);
return -1;
}
/* Currently every domain supports the same number of max cpus
* as that supported by vbox and thus take it directly from
* the systemproperties.
*/
gVBoxAPI.UIVirtualBox.GetSystemProperties(data->vboxObj, &systemProperties);
if (systemProperties) {
gVBoxAPI.UISystemProperties.GetMaxGuestCPUCount(systemProperties, &maxCPUCount);
VBOX_RELEASE(systemProperties);
}
if (maxCPUCount > 0)
ret = maxCPUCount;
return ret;
}
static int vboxDomainGetMaxVcpus(virDomainPtr dom)
{
return vboxDomainGetVcpusFlags(dom, (VIR_DOMAIN_AFFECT_LIVE |
VIR_DOMAIN_VCPU_MAXIMUM));
}
static void
vboxHostDeviceGetXMLDesc(vboxGlobalData *data, virDomainDefPtr def, IMachine *machine)
{
IUSBCommon *USBCommon = NULL;
PRBool enabled = PR_FALSE;
vboxArray deviceFilters = VBOX_ARRAY_INITIALIZER;
size_t i;
PRUint32 USBFilterCount = 0;
def->nhostdevs = 0;
gVBoxAPI.UIMachine.GetUSBCommon(machine, &USBCommon);
if (!USBCommon)
return;
gVBoxAPI.UIUSBCommon.GetEnabled(USBCommon, &enabled);
if (!enabled)
goto release_controller;
gVBoxAPI.UArray.vboxArrayGet(&deviceFilters, USBCommon,
gVBoxAPI.UArray.handleUSBGetDeviceFilters(USBCommon));
if (deviceFilters.count <= 0)
goto release_filters;
/* check if the filters are active and then only
* alloc mem and set def->nhostdevs
*/
for (i = 0; i < deviceFilters.count; i++) {
PRBool active = PR_FALSE;
IUSBDeviceFilter *deviceFilter = deviceFilters.items[i];
gVBoxAPI.UIUSBDeviceFilter.GetActive(deviceFilter, &active);
if (active) {
def->nhostdevs++;
}
}
if (def->nhostdevs == 0)
goto release_filters;
/* Alloc mem needed for the filters now */
if (VIR_ALLOC_N(def->hostdevs, def->nhostdevs) < 0)
goto release_filters;
for (i = 0; i < def->nhostdevs; i++) {
def->hostdevs[i] = virDomainHostdevDefAlloc();
if (!def->hostdevs[i])
goto release_hostdevs;
}
for (i = 0; i < deviceFilters.count; i++) {
PRBool active = PR_FALSE;
IUSBDeviceFilter *deviceFilter = deviceFilters.items[i];
PRUnichar *vendorIdUtf16 = NULL;
char *vendorIdUtf8 = NULL;
unsigned vendorId = 0;
PRUnichar *productIdUtf16 = NULL;
char *productIdUtf8 = NULL;
unsigned productId = 0;
char *endptr = NULL;
gVBoxAPI.UIUSBDeviceFilter.GetActive(deviceFilter, &active);
if (!active)
continue;
def->hostdevs[USBFilterCount]->mode =
VIR_DOMAIN_HOSTDEV_MODE_SUBSYS;
def->hostdevs[USBFilterCount]->source.subsys.type =
VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB;
gVBoxAPI.UIUSBDeviceFilter.GetVendorId(deviceFilter, &vendorIdUtf16);
gVBoxAPI.UIUSBDeviceFilter.GetProductId(deviceFilter, &productIdUtf16);
VBOX_UTF16_TO_UTF8(vendorIdUtf16, &vendorIdUtf8);
VBOX_UTF16_TO_UTF8(productIdUtf16, &productIdUtf8);
ignore_value(virStrToLong_ui(vendorIdUtf8, &endptr, 16, &vendorId));
ignore_value(virStrToLong_ui(productIdUtf8, &endptr, 16, &productId));
def->hostdevs[USBFilterCount]->source.subsys.u.usb.vendor = vendorId;
def->hostdevs[USBFilterCount]->source.subsys.u.usb.product = productId;
VBOX_UTF16_FREE(vendorIdUtf16);
VBOX_UTF8_FREE(vendorIdUtf8);
VBOX_UTF16_FREE(productIdUtf16);
VBOX_UTF8_FREE(productIdUtf8);
USBFilterCount++;
}
release_filters:
gVBoxAPI.UArray.vboxArrayRelease(&deviceFilters);
release_controller:
VBOX_RELEASE(USBCommon);
return;
release_hostdevs:
for (i = 0; i < def->nhostdevs; i++)
virDomainHostdevDefFree(def->hostdevs[i]);
VIR_FREE(def->hostdevs);
goto release_filters;
}
static void
vboxDumpIDEHDDsNew(virDomainDefPtr def, vboxGlobalData *data, IMachine *machine)
{
/* dump IDE hdds if present */
vboxArray mediumAttachments = VBOX_ARRAY_INITIALIZER;
bool error = false;
int diskCount = 0;
size_t i;
PRUint32 maxPortPerInst[StorageBus_Floppy + 1] = {};
PRUint32 maxSlotPerPort[StorageBus_Floppy + 1] = {};
if (gVBoxAPI.oldMediumInterface)
VIR_WARN("This function may not work in current vbox version");
def->ndisks = 0;
gVBoxAPI.UArray.vboxArrayGet(&mediumAttachments, machine,
gVBoxAPI.UArray.handleMachineGetMediumAttachments(machine));
/* get the number of attachments */
for (i = 0; i < mediumAttachments.count; i++) {
IMediumAttachment *imediumattach = mediumAttachments.items[i];
if (imediumattach) {
IMedium *medium = NULL;
gVBoxAPI.UIMediumAttachment.GetMedium(imediumattach, &medium);
if (medium) {
def->ndisks++;
VBOX_RELEASE(medium);
}
}
}
/* Allocate mem, if fails return error */
if (VIR_ALLOC_N(def->disks, def->ndisks) >= 0) {
for (i = 0; i < def->ndisks; i++) {
virDomainDiskDefPtr disk = virDomainDiskDefNew();
if (!disk) {
error = true;
break;
}
def->disks[i] = disk;
}
} else {
error = true;
}
if (!error)
error = !vboxGetMaxPortSlotValues(data->vboxObj, maxPortPerInst, maxSlotPerPort);
/* get the attachment details here */
for (i = 0; i < mediumAttachments.count && diskCount < def->ndisks && !error; i++) {
IMediumAttachment *imediumattach = mediumAttachments.items[i];
IStorageController *storageController = NULL;
PRUnichar *storageControllerName = NULL;
PRUint32 deviceType = DeviceType_Null;
PRUint32 storageBus = StorageBus_Null;
PRBool readOnly = PR_FALSE;
IMedium *medium = NULL;
PRUnichar *mediumLocUtf16 = NULL;
char *mediumLocUtf8 = NULL;
PRUint32 deviceInst = 0;
PRInt32 devicePort = 0;
PRInt32 deviceSlot = 0;
if (!imediumattach)
continue;
gVBoxAPI.UIMediumAttachment.GetMedium(imediumattach, &medium);
if (!medium)
continue;
gVBoxAPI.UIMediumAttachment.GetController(imediumattach, &storageControllerName);
if (!storageControllerName) {
VBOX_RELEASE(medium);
continue;
}
gVBoxAPI.UIMachine.GetStorageControllerByName(machine,
storageControllerName,
&storageController);
VBOX_UTF16_FREE(storageControllerName);
if (!storageController) {
VBOX_RELEASE(medium);
continue;
}
gVBoxAPI.UIMedium.GetLocation(medium, &mediumLocUtf16);
VBOX_UTF16_TO_UTF8(mediumLocUtf16, &mediumLocUtf8);
VBOX_UTF16_FREE(mediumLocUtf16);
ignore_value(virDomainDiskSetSource(def->disks[diskCount],
mediumLocUtf8));
VBOX_UTF8_FREE(mediumLocUtf8);
if (!virDomainDiskGetSource(def->disks[diskCount])) {
VBOX_RELEASE(medium);
VBOX_RELEASE(storageController);
error = true;
break;
}
gVBoxAPI.UIStorageController.GetBus(storageController, &storageBus);
if (storageBus == StorageBus_IDE) {
def->disks[diskCount]->bus = VIR_DOMAIN_DISK_BUS_IDE;
} else if (storageBus == StorageBus_SATA) {
def->disks[diskCount]->bus = VIR_DOMAIN_DISK_BUS_SATA;
} else if (storageBus == StorageBus_SCSI) {
def->disks[diskCount]->bus = VIR_DOMAIN_DISK_BUS_SCSI;
} else if (storageBus == StorageBus_Floppy) {
def->disks[diskCount]->bus = VIR_DOMAIN_DISK_BUS_FDC;
}
gVBoxAPI.UIMediumAttachment.GetType(imediumattach, &deviceType);
if (deviceType == DeviceType_HardDisk)
def->disks[diskCount]->device = VIR_DOMAIN_DISK_DEVICE_DISK;
else if (deviceType == DeviceType_Floppy)
def->disks[diskCount]->device = VIR_DOMAIN_DISK_DEVICE_FLOPPY;
else if (deviceType == DeviceType_DVD)
def->disks[diskCount]->device = VIR_DOMAIN_DISK_DEVICE_CDROM;
gVBoxAPI.UIMediumAttachment.GetPort(imediumattach, &devicePort);
gVBoxAPI.UIMediumAttachment.GetDevice(imediumattach, &deviceSlot);
def->disks[diskCount]->dst = vboxGenerateMediumName(storageBus,
deviceInst,
devicePort,
deviceSlot,
maxPortPerInst,
maxSlotPerPort);
if (!def->disks[diskCount]->dst) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Could not generate medium name for the disk "
"at: controller instance:%u, port:%d, slot:%d"),
deviceInst, devicePort, deviceSlot);
VBOX_RELEASE(medium);
VBOX_RELEASE(storageController);
error = true;
break;
}
gVBoxAPI.UIMedium.GetReadOnly(medium, &readOnly);
if (readOnly == PR_TRUE)
def->disks[diskCount]->src->readonly = true;
virDomainDiskSetType(def->disks[diskCount],
VIR_STORAGE_TYPE_FILE);
VBOX_RELEASE(medium);
VBOX_RELEASE(storageController);
diskCount++;
}
gVBoxAPI.UArray.vboxArrayRelease(&mediumAttachments);
/* cleanup on error */
if (error) {
for (i = 0; i < def->ndisks; i++) {
VIR_FREE(def->disks[i]);
}
VIR_FREE(def->disks);
def->ndisks = 0;
}
}
static void
vboxDumpVideo(virDomainDefPtr def, vboxGlobalData *data ATTRIBUTE_UNUSED,
IMachine *machine)
{
/* dump video options vram/2d/3d/directx/etc. */
/* Currently supports only one graphics card */
def->nvideos = 1;
if (VIR_ALLOC_N(def->videos, def->nvideos) >= 0) {
if (VIR_ALLOC(def->videos[0]) >= 0) {
/* the default is: vram is 8MB, One monitor, 3dAccel Off */
PRUint32 VRAMSize = 8;
PRUint32 monitorCount = 1;
PRBool accelerate3DEnabled = PR_FALSE;
PRBool accelerate2DEnabled = PR_FALSE;
gVBoxAPI.UIMachine.GetVRAMSize(machine, &VRAMSize);
gVBoxAPI.UIMachine.GetMonitorCount(machine, &monitorCount);
gVBoxAPI.UIMachine.GetAccelerate3DEnabled(machine, &accelerate3DEnabled);
if (gVBoxAPI.accelerate2DVideo)
gVBoxAPI.UIMachine.GetAccelerate2DVideoEnabled(machine, &accelerate2DEnabled);
def->videos[0]->type = VIR_DOMAIN_VIDEO_TYPE_VBOX;
def->videos[0]->vram = VRAMSize * 1024;
def->videos[0]->heads = monitorCount;
if (VIR_ALLOC(def->videos[0]->accel) >= 0) {
def->videos[0]->accel->support3d = accelerate3DEnabled;
def->videos[0]->accel->support2d = accelerate2DEnabled;
}
}
}
}
static void
vboxDumpDisplay(virDomainDefPtr def, vboxGlobalData *data, IMachine *machine)
{
/* dump display options vrdp/gui/sdl */
int vrdpPresent = 0;
int sdlPresent = 0;
int guiPresent = 0;
int totalPresent = 0;
char *guiDisplay = NULL;
char *sdlDisplay = NULL;
PRUnichar *keyTypeUtf16 = NULL;
PRUnichar *valueTypeUtf16 = NULL;
char *valueTypeUtf8 = NULL;
IVRDxServer *VRDxServer = NULL;
PRBool VRDxEnabled = PR_FALSE;
def->ngraphics = 0;
VBOX_UTF8_TO_UTF16("FRONTEND/Type", &keyTypeUtf16);
gVBoxAPI.UIMachine.GetExtraData(machine, keyTypeUtf16, &valueTypeUtf16);
VBOX_UTF16_FREE(keyTypeUtf16);
if (valueTypeUtf16) {
VBOX_UTF16_TO_UTF8(valueTypeUtf16, &valueTypeUtf8);
VBOX_UTF16_FREE(valueTypeUtf16);
if (STREQ(valueTypeUtf8, "sdl") || STREQ(valueTypeUtf8, "gui")) {
PRUnichar *keyDislpayUtf16 = NULL;
PRUnichar *valueDisplayUtf16 = NULL;
char *valueDisplayUtf8 = NULL;
VBOX_UTF8_TO_UTF16("FRONTEND/Display", &keyDislpayUtf16);
gVBoxAPI.UIMachine.GetExtraData(machine, keyDislpayUtf16, &valueDisplayUtf16);
VBOX_UTF16_FREE(keyDislpayUtf16);
if (valueDisplayUtf16) {
VBOX_UTF16_TO_UTF8(valueDisplayUtf16, &valueDisplayUtf8);
VBOX_UTF16_FREE(valueDisplayUtf16);
if (strlen(valueDisplayUtf8) <= 0)
VBOX_UTF8_FREE(valueDisplayUtf8);
}
if (STREQ(valueTypeUtf8, "sdl")) {
sdlPresent = 1;
if (VIR_STRDUP(sdlDisplay, valueDisplayUtf8) < 0) {
/* just don't go to cleanup yet as it is ok to have
* sdlDisplay as NULL and we check it below if it
* exist and then only use it there
*/
}
totalPresent++;
}
if (STREQ(valueTypeUtf8, "gui")) {
guiPresent = 1;
if (VIR_STRDUP(guiDisplay, valueDisplayUtf8) < 0) {
/* just don't go to cleanup yet as it is ok to have
* guiDisplay as NULL and we check it below if it
* exist and then only use it there
*/
}
totalPresent++;
}
VBOX_UTF8_FREE(valueDisplayUtf8);
}
if (STREQ(valueTypeUtf8, "vrdp"))
vrdpPresent = 1;
VBOX_UTF8_FREE(valueTypeUtf8);
}
if ((totalPresent > 0) && (VIR_ALLOC_N(def->graphics, totalPresent) >= 0)) {
if ((guiPresent) && (VIR_ALLOC(def->graphics[def->ngraphics]) >= 0)) {
def->graphics[def->ngraphics]->type = VIR_DOMAIN_GRAPHICS_TYPE_DESKTOP;
if (guiDisplay)
def->graphics[def->ngraphics]->data.desktop.display = guiDisplay;
def->ngraphics++;
}
if ((sdlPresent) && (VIR_ALLOC(def->graphics[def->ngraphics]) >= 0)) {
def->graphics[def->ngraphics]->type = VIR_DOMAIN_GRAPHICS_TYPE_SDL;
if (sdlDisplay)
def->graphics[def->ngraphics]->data.sdl.display = sdlDisplay;
def->ngraphics++;
}
} else if ((vrdpPresent != 1) && (totalPresent == 0) && (VIR_ALLOC_N(def->graphics, 1) >= 0)) {
if (VIR_ALLOC(def->graphics[def->ngraphics]) >= 0) {
const char *tmp;
def->graphics[def->ngraphics]->type = VIR_DOMAIN_GRAPHICS_TYPE_DESKTOP;
tmp = virGetEnvBlockSUID("DISPLAY");
if (VIR_STRDUP(def->graphics[def->ngraphics]->data.desktop.display, tmp) < 0) {
/* just don't go to cleanup yet as it is ok to have
* display as NULL
*/
}
totalPresent++;
def->ngraphics++;
}
}
gVBoxAPI.UIMachine.GetVRDxServer(machine, &VRDxServer);
if (VRDxServer) {
gVBoxAPI.UIVRDxServer.GetEnabled(VRDxServer, &VRDxEnabled);
if (VRDxEnabled) {
totalPresent++;
if ((VIR_REALLOC_N(def->graphics, totalPresent) >= 0) &&
(VIR_ALLOC(def->graphics[def->ngraphics]) >= 0)) {
PRUnichar *netAddressUtf16 = NULL;
char *netAddressUtf8 = NULL;
PRBool allowMultiConnection = PR_FALSE;
PRBool reuseSingleConnection = PR_FALSE;
gVBoxAPI.UIVRDxServer.GetPorts(data, VRDxServer, def->graphics[def->ngraphics]);
def->graphics[def->ngraphics]->type = VIR_DOMAIN_GRAPHICS_TYPE_RDP;
gVBoxAPI.UIVRDxServer.GetNetAddress(data, VRDxServer, &netAddressUtf16);
if (netAddressUtf16) {
VBOX_UTF16_TO_UTF8(netAddressUtf16, &netAddressUtf8);
if (STRNEQ(netAddressUtf8, ""))
virDomainGraphicsListenSetAddress(def->graphics[def->ngraphics], 0,
netAddressUtf8, -1, true);
VBOX_UTF16_FREE(netAddressUtf16);
VBOX_UTF8_FREE(netAddressUtf8);
}
gVBoxAPI.UIVRDxServer.GetAllowMultiConnection(VRDxServer, &allowMultiConnection);
if (allowMultiConnection) {
def->graphics[def->ngraphics]->data.rdp.multiUser = true;
}
gVBoxAPI.UIVRDxServer.GetReuseSingleConnection(VRDxServer, &reuseSingleConnection);
if (reuseSingleConnection) {
def->graphics[def->ngraphics]->data.rdp.replaceUser = true;
}
def->ngraphics++;
} else {
virReportOOMError();
}
}
VBOX_RELEASE(VRDxServer);
}
}
static void
vboxDumpSharedFolders(virDomainDefPtr def, vboxGlobalData *data, IMachine *machine)
{
/* shared folders */
vboxArray sharedFolders = VBOX_ARRAY_INITIALIZER;
size_t i = 0;
def->nfss = 0;
gVBoxAPI.UArray.vboxArrayGet(&sharedFolders, machine,
gVBoxAPI.UArray.handleMachineGetSharedFolders(machine));
if (sharedFolders.count <= 0)
goto sharedFoldersCleanup;
if (VIR_ALLOC_N(def->fss, sharedFolders.count) < 0)
goto sharedFoldersCleanup;
for (i = 0; i < sharedFolders.count; i++) {
ISharedFolder *sharedFolder = sharedFolders.items[i];
PRUnichar *nameUtf16 = NULL;
char *name = NULL;
PRUnichar *hostPathUtf16 = NULL;
char *hostPath = NULL;
PRBool writable = PR_FALSE;
if (VIR_ALLOC(def->fss[i]) < 0)
goto sharedFoldersCleanup;
def->fss[i]->type = VIR_DOMAIN_FS_TYPE_MOUNT;
gVBoxAPI.UISharedFolder.GetHostPath(sharedFolder, &hostPathUtf16);
VBOX_UTF16_TO_UTF8(hostPathUtf16, &hostPath);
if (VIR_STRDUP(def->fss[i]->src, hostPath) < 0) {
VBOX_UTF8_FREE(hostPath);
VBOX_UTF16_FREE(hostPathUtf16);
goto sharedFoldersCleanup;
}
VBOX_UTF8_FREE(hostPath);
VBOX_UTF16_FREE(hostPathUtf16);
gVBoxAPI.UISharedFolder.GetName(sharedFolder, &nameUtf16);
VBOX_UTF16_TO_UTF8(nameUtf16, &name);
if (VIR_STRDUP(def->fss[i]->dst, name) < 0) {
VBOX_UTF8_FREE(name);
VBOX_UTF16_FREE(nameUtf16);
goto sharedFoldersCleanup;
}
VBOX_UTF8_FREE(name);
VBOX_UTF16_FREE(nameUtf16);
gVBoxAPI.UISharedFolder.GetWritable(sharedFolder, &writable);
def->fss[i]->readonly = !writable;
++def->nfss;
}
sharedFoldersCleanup:
gVBoxAPI.UArray.vboxArrayRelease(&sharedFolders);
}
static void
vboxDumpNetwork(virDomainDefPtr def, vboxGlobalData *data, IMachine *machine, PRUint32 networkAdapterCount)
{
PRUint32 netAdpIncCnt = 0;
size_t i = 0;
/* dump network cards if present */
def->nnets = 0;
/* Get which network cards are enabled */
for (i = 0; i < networkAdapterCount; i++) {
INetworkAdapter *adapter = NULL;
gVBoxAPI.UIMachine.GetNetworkAdapter(machine, i, &adapter);
if (adapter) {
PRBool enabled = PR_FALSE;
gVBoxAPI.UINetworkAdapter.GetEnabled(adapter, &enabled);
if (enabled) {
def->nnets++;
}
VBOX_RELEASE(adapter);
}
}
/* Allocate memory for the networkcards which are enabled */
if ((def->nnets > 0) && (VIR_ALLOC_N(def->nets, def->nnets) >= 0)) {
for (i = 0; i < def->nnets; i++) {
ignore_value(VIR_ALLOC(def->nets[i]));
}
}
/* Now get the details about the network cards here */
for (i = 0; netAdpIncCnt < def->nnets && i < networkAdapterCount; i++) {
INetworkAdapter *adapter = NULL;
gVBoxAPI.UIMachine.GetNetworkAdapter(machine, i, &adapter);
if (adapter) {
PRBool enabled = PR_FALSE;
gVBoxAPI.UINetworkAdapter.GetEnabled(adapter, &enabled);
if (enabled) {
PRUint32 attachmentType = NetworkAttachmentType_Null;
PRUint32 adapterType = NetworkAdapterType_Null;
PRUnichar *MACAddressUtf16 = NULL;
char *MACAddress = NULL;
char macaddr[VIR_MAC_STRING_BUFLEN] = {0};
gVBoxAPI.UINetworkAdapter.GetAttachmentType(adapter, &attachmentType);
if (attachmentType == NetworkAttachmentType_NAT) {
def->nets[netAdpIncCnt]->type = VIR_DOMAIN_NET_TYPE_USER;
} else if (attachmentType == NetworkAttachmentType_Bridged) {
PRUnichar *hostIntUtf16 = NULL;
char *hostInt = NULL;
def->nets[netAdpIncCnt]->type = VIR_DOMAIN_NET_TYPE_BRIDGE;
gVBoxAPI.UINetworkAdapter.GetBridgedInterface(adapter, &hostIntUtf16);
VBOX_UTF16_TO_UTF8(hostIntUtf16, &hostInt);
ignore_value(VIR_STRDUP(def->nets[netAdpIncCnt]->data.bridge.brname, hostInt));
VBOX_UTF8_FREE(hostInt);
VBOX_UTF16_FREE(hostIntUtf16);
} else if (attachmentType == NetworkAttachmentType_Internal) {
PRUnichar *intNetUtf16 = NULL;
char *intNet = NULL;
def->nets[netAdpIncCnt]->type = VIR_DOMAIN_NET_TYPE_INTERNAL;
gVBoxAPI.UINetworkAdapter.GetInternalNetwork(adapter, &intNetUtf16);
VBOX_UTF16_TO_UTF8(intNetUtf16, &intNet);
ignore_value(VIR_STRDUP(def->nets[netAdpIncCnt]->data.internal.name, intNet));
VBOX_UTF8_FREE(intNet);
VBOX_UTF16_FREE(intNetUtf16);
} else if (attachmentType == NetworkAttachmentType_HostOnly) {
PRUnichar *hostIntUtf16 = NULL;
char *hostInt = NULL;
def->nets[netAdpIncCnt]->type = VIR_DOMAIN_NET_TYPE_NETWORK;
gVBoxAPI.UINetworkAdapter.GetHostOnlyInterface(adapter, &hostIntUtf16);
VBOX_UTF16_TO_UTF8(hostIntUtf16, &hostInt);
ignore_value(VIR_STRDUP(def->nets[netAdpIncCnt]->data.network.name, hostInt));
VBOX_UTF8_FREE(hostInt);
VBOX_UTF16_FREE(hostIntUtf16);
} else {
/* default to user type i.e. NAT in VirtualBox if this
* dump is ever used to create a machine.
*/
def->nets[netAdpIncCnt]->type = VIR_DOMAIN_NET_TYPE_USER;
}
gVBoxAPI.UINetworkAdapter.GetAdapterType(adapter, &adapterType);
if (adapterType == NetworkAdapterType_Am79C970A) {
ignore_value(VIR_STRDUP(def->nets[netAdpIncCnt]->model, "Am79C970A"));
} else if (adapterType == NetworkAdapterType_Am79C973) {
ignore_value(VIR_STRDUP(def->nets[netAdpIncCnt]->model, "Am79C973"));
} else if (adapterType == NetworkAdapterType_I82540EM) {
ignore_value(VIR_STRDUP(def->nets[netAdpIncCnt]->model, "82540EM"));
} else if (adapterType == NetworkAdapterType_I82545EM) {
ignore_value(VIR_STRDUP(def->nets[netAdpIncCnt]->model, "82545EM"));
} else if (adapterType == NetworkAdapterType_I82543GC) {
ignore_value(VIR_STRDUP(def->nets[netAdpIncCnt]->model, "82543GC"));
} else if (gVBoxAPI.APIVersion >= 3000051 &&
adapterType == NetworkAdapterType_Virtio) {
/* Only vbox 3.1 and later support NetworkAdapterType_Virto */
ignore_value(VIR_STRDUP(def->nets[netAdpIncCnt]->model, "virtio"));
}
gVBoxAPI.UINetworkAdapter.GetMACAddress(adapter, &MACAddressUtf16);
VBOX_UTF16_TO_UTF8(MACAddressUtf16, &MACAddress);
snprintf(macaddr, VIR_MAC_STRING_BUFLEN,
"%c%c:%c%c:%c%c:%c%c:%c%c:%c%c",
MACAddress[0], MACAddress[1], MACAddress[2], MACAddress[3],
MACAddress[4], MACAddress[5], MACAddress[6], MACAddress[7],
MACAddress[8], MACAddress[9], MACAddress[10], MACAddress[11]);
/* XXX some real error handling here some day ... */
ignore_value(virMacAddrParse(macaddr,
&def->nets[netAdpIncCnt]->mac));
netAdpIncCnt++;
VBOX_UTF16_FREE(MACAddressUtf16);
VBOX_UTF8_FREE(MACAddress);
}
VBOX_RELEASE(adapter);
}
}
}
static void
vboxDumpAudio(virDomainDefPtr def, vboxGlobalData *data ATTRIBUTE_UNUSED,
IMachine *machine)
{
/* dump sound card if active */
/* Set def->nsounds to one as VirtualBox currently supports
* only one sound card
*/
IAudioAdapter *audioAdapter = NULL;
gVBoxAPI.UIMachine.GetAudioAdapter(machine, &audioAdapter);
if (audioAdapter) {
PRBool enabled = PR_FALSE;
gVBoxAPI.UIAudioAdapter.GetEnabled(audioAdapter, &enabled);
if (enabled) {
PRUint32 audioController = AudioControllerType_AC97;
def->nsounds = 1;
if (VIR_ALLOC_N(def->sounds, def->nsounds) >= 0) {
if (VIR_ALLOC(def->sounds[0]) >= 0) {
gVBoxAPI.UIAudioAdapter.GetAudioController(audioAdapter, &audioController);
if (audioController == AudioControllerType_SB16) {
def->sounds[0]->model = VIR_DOMAIN_SOUND_MODEL_SB16;
} else if (audioController == AudioControllerType_AC97) {
def->sounds[0]->model = VIR_DOMAIN_SOUND_MODEL_AC97;
}
} else {
VIR_FREE(def->sounds);
def->nsounds = 0;
}
} else {
def->nsounds = 0;
}
}
VBOX_RELEASE(audioAdapter);
}
}
static void
vboxDumpSerial(virDomainDefPtr def, vboxGlobalData *data, IMachine *machine, PRUint32 serialPortCount)
{
PRUint32 serialPortIncCount = 0;
size_t i = 0;
/* dump serial port if active */
def->nserials = 0;
/* Get which serial ports are enabled/active */
for (i = 0; i < serialPortCount; i++) {
ISerialPort *serialPort = NULL;
gVBoxAPI.UIMachine.GetSerialPort(machine, i, &serialPort);
if (serialPort) {
PRBool enabled = PR_FALSE;
gVBoxAPI.UISerialPort.GetEnabled(serialPort, &enabled);
if (enabled) {
def->nserials++;
}
VBOX_RELEASE(serialPort);
}
}
/* Allocate memory for the serial ports which are enabled */
if ((def->nserials > 0) && (VIR_ALLOC_N(def->serials, def->nserials) >= 0)) {
for (i = 0; i < def->nserials; i++) {
ignore_value(VIR_ALLOC(def->serials[i]));
}
}
/* Now get the details about the serial ports here */
for (i = 0;
serialPortIncCount < def->nserials && i < serialPortCount;
i++) {
ISerialPort *serialPort = NULL;
gVBoxAPI.UIMachine.GetSerialPort(machine, i, &serialPort);
if (serialPort) {
PRBool enabled = PR_FALSE;
gVBoxAPI.UISerialPort.GetEnabled(serialPort, &enabled);
if (enabled) {
PRUint32 hostMode = PortMode_Disconnected;
PRUint32 IOBase = 0;
PRUint32 IRQ = 0;
PRUnichar *pathUtf16 = NULL;
char *path = NULL;
gVBoxAPI.UISerialPort.GetHostMode(serialPort, &hostMode);
if (hostMode == PortMode_HostPipe) {
def->serials[serialPortIncCount]->source.type = VIR_DOMAIN_CHR_TYPE_PIPE;
} else if (hostMode == PortMode_HostDevice) {
def->serials[serialPortIncCount]->source.type = VIR_DOMAIN_CHR_TYPE_DEV;
} else if (gVBoxAPI.APIVersion >= 2002051 &&
hostMode == PortMode_RawFile) {
/* PortMode RawFile is used for vbox 3.0 or later */
def->serials[serialPortIncCount]->source.type = VIR_DOMAIN_CHR_TYPE_FILE;
} else {
def->serials[serialPortIncCount]->source.type = VIR_DOMAIN_CHR_TYPE_NULL;
}
def->serials[serialPortIncCount]->deviceType = VIR_DOMAIN_CHR_DEVICE_TYPE_SERIAL;
gVBoxAPI.UISerialPort.GetIRQ(serialPort, &IRQ);
gVBoxAPI.UISerialPort.GetIOBase(serialPort, &IOBase);
if ((IRQ == 4) && (IOBase == 1016)) {
def->serials[serialPortIncCount]->target.port = 0;
} else if ((IRQ == 3) && (IOBase == 760)) {
def->serials[serialPortIncCount]->target.port = 1;
}
gVBoxAPI.UISerialPort.GetPath(serialPort, &pathUtf16);
if (pathUtf16) {
VBOX_UTF16_TO_UTF8(pathUtf16, &path);
ignore_value(VIR_STRDUP(def->serials[serialPortIncCount]->source.data.file.path, path));
}
serialPortIncCount++;
VBOX_UTF16_FREE(pathUtf16);
VBOX_UTF8_FREE(path);
}
VBOX_RELEASE(serialPort);
}
}
}
static void
vboxDumpParallel(virDomainDefPtr def, vboxGlobalData *data, IMachine *machine, PRUint32 parallelPortCount)
{
PRUint32 parallelPortIncCount = 0;
size_t i = 0;
/* dump parallel ports if active */
def->nparallels = 0;
/* Get which parallel ports are enabled/active */
for (i = 0; i < parallelPortCount; i++) {
IParallelPort *parallelPort = NULL;
gVBoxAPI.UIMachine.GetParallelPort(machine, i, &parallelPort);
if (parallelPort) {
PRBool enabled = PR_FALSE;
gVBoxAPI.UIParallelPort.GetEnabled(parallelPort, &enabled);
if (enabled) {
def->nparallels++;
}
VBOX_RELEASE(parallelPort);
}
}
/* Allocate memory for the parallel ports which are enabled */
if ((def->nparallels > 0) && (VIR_ALLOC_N(def->parallels, def->nparallels) >= 0)) {
for (i = 0; i < def->nparallels; i++) {
ignore_value(VIR_ALLOC(def->parallels[i]));
}
}
/* Now get the details about the parallel ports here */
for (i = 0;
parallelPortIncCount < def->nparallels &&
i < parallelPortCount;
i++) {
IParallelPort *parallelPort = NULL;
gVBoxAPI.UIMachine.GetParallelPort(machine, i, &parallelPort);
if (parallelPort) {
PRBool enabled = PR_FALSE;
gVBoxAPI.UIParallelPort.GetEnabled(parallelPort, &enabled);
if (enabled) {
PRUint32 IOBase = 0;
PRUint32 IRQ = 0;
PRUnichar *pathUtf16 = NULL;
char *path = NULL;
gVBoxAPI.UIParallelPort.GetIRQ(parallelPort, &IRQ);
gVBoxAPI.UIParallelPort.GetIOBase(parallelPort, &IOBase);
if ((IRQ == 7) && (IOBase == 888)) {
def->parallels[parallelPortIncCount]->target.port = 0;
} else if ((IRQ == 5) && (IOBase == 632)) {
def->parallels[parallelPortIncCount]->target.port = 1;
}
def->parallels[parallelPortIncCount]->source.type = VIR_DOMAIN_CHR_TYPE_FILE;
def->parallels[parallelPortIncCount]->deviceType = VIR_DOMAIN_CHR_DEVICE_TYPE_PARALLEL;
gVBoxAPI.UIParallelPort.GetPath(parallelPort, &pathUtf16);
VBOX_UTF16_TO_UTF8(pathUtf16, &path);
ignore_value(VIR_STRDUP(def->parallels[parallelPortIncCount]->source.data.file.path, path));
parallelPortIncCount++;
VBOX_UTF16_FREE(pathUtf16);
VBOX_UTF8_FREE(path);
}
VBOX_RELEASE(parallelPort);
}
}
}
static char *vboxDomainGetXMLDesc(virDomainPtr dom, unsigned int flags)
{
VBOX_OBJECT_CHECK(dom->conn, char *, NULL);
virDomainDefPtr def = NULL;
IMachine *machine = NULL;
vboxIIDUnion iid;
PRBool accessible = PR_FALSE;
size_t i = 0;
PRBool PAEEnabled = PR_FALSE;
PRBool ACPIEnabled = PR_FALSE;
PRBool IOAPICEnabled = PR_FALSE;
PRUint32 CPUCount = 0;
PRUint32 memorySize = 0;
PRUint32 networkAdapterCount = 0;
PRUint32 maxMemorySize = 4 * 1024;
PRUint32 maxBootPosition = 0;
PRUint32 serialPortCount = 0;
PRUint32 parallelPortCount = 0;
IBIOSSettings *bios = NULL;
PRUint32 chipsetType = ChipsetType_Null;
ISystemProperties *systemProperties = NULL;
/* Flags checked by virDomainDefFormat */
if (openSessionForMachine(data, dom->uuid, &iid, &machine, false) < 0)
goto cleanup;
if (VIR_ALLOC(def) < 0)
goto cleanup;
gVBoxAPI.UIMachine.GetAccessible(machine, &accessible);
if (!accessible)
goto cleanup;
def->virtType = VIR_DOMAIN_VIRT_VBOX;
def->id = dom->id;
memcpy(def->uuid, dom->uuid, VIR_UUID_BUFLEN);
if (VIR_STRDUP(def->name, dom->name) < 0)
goto cleanup;
gVBoxAPI.UIMachine.GetMemorySize(machine, &memorySize);
def->mem.cur_balloon = memorySize * 1024;
if (gVBoxAPI.chipsetType)
gVBoxAPI.UIMachine.GetChipsetType(machine, &chipsetType);
gVBoxAPI.UIVirtualBox.GetSystemProperties(data->vboxObj, &systemProperties);
if (systemProperties) {
gVBoxAPI.UISystemProperties.GetMaxGuestRAM(systemProperties, &maxMemorySize);
gVBoxAPI.UISystemProperties.GetMaxBootPosition(systemProperties, &maxBootPosition);
gVBoxAPI.UISystemProperties.GetMaxNetworkAdapters(systemProperties, chipsetType, &networkAdapterCount);
gVBoxAPI.UISystemProperties.GetSerialPortCount(systemProperties, &serialPortCount);
gVBoxAPI.UISystemProperties.GetParallelPortCount(systemProperties, &parallelPortCount);
VBOX_RELEASE(systemProperties);
systemProperties = NULL;
}
/* Currently setting memory and maxMemory as same, cause
* the notation here seems to be inconsistent while
* reading and while dumping xml
*/
/* def->mem.max_balloon = maxMemorySize * 1024; */
def->mem.max_balloon = memorySize * 1024;
gVBoxAPI.UIMachine.GetCPUCount(machine, &CPUCount);
def->maxvcpus = def->vcpus = CPUCount;
/* Skip cpumasklen, cpumask, onReboot, onPoweroff, onCrash */
if (VIR_STRDUP(def->os.type, "hvm") < 0)
goto cleanup;
def->os.arch = virArchFromHost();
def->os.nBootDevs = 0;
for (i = 0; (i < VIR_DOMAIN_BOOT_LAST) && (i < maxBootPosition); i++) {
PRUint32 device = DeviceType_Null;
gVBoxAPI.UIMachine.GetBootOrder(machine, i+1, &device);
if (device == DeviceType_Floppy) {
def->os.bootDevs[i] = VIR_DOMAIN_BOOT_FLOPPY;
def->os.nBootDevs++;
} else if (device == DeviceType_DVD) {
def->os.bootDevs[i] = VIR_DOMAIN_BOOT_CDROM;
def->os.nBootDevs++;
} else if (device == DeviceType_HardDisk) {
def->os.bootDevs[i] = VIR_DOMAIN_BOOT_DISK;
def->os.nBootDevs++;
} else if (device == DeviceType_Network) {
def->os.bootDevs[i] = VIR_DOMAIN_BOOT_NET;
def->os.nBootDevs++;
} else if (device == DeviceType_USB) {
/* Not supported by libvirt yet */
} else if (device == DeviceType_SharedFolder) {
/* Not supported by libvirt yet */
/* Can VirtualBox really boot from a shared folder? */
}
}
gVBoxAPI.UIMachine.GetCPUProperty(machine, CPUPropertyType_PAE, &PAEEnabled);
if (PAEEnabled)
def->features[VIR_DOMAIN_FEATURE_PAE] = VIR_TRISTATE_SWITCH_ON;
gVBoxAPI.UIMachine.GetBIOSSettings(machine, &bios);
if (bios) {
gVBoxAPI.UIBIOSSettings.GetACPIEnabled(bios, &ACPIEnabled);
if (ACPIEnabled)
def->features[VIR_DOMAIN_FEATURE_ACPI] = VIR_TRISTATE_SWITCH_ON;
gVBoxAPI.UIBIOSSettings.GetIOAPICEnabled(bios, &IOAPICEnabled);
if (IOAPICEnabled)
def->features[VIR_DOMAIN_FEATURE_APIC] = VIR_TRISTATE_SWITCH_ON;
VBOX_RELEASE(bios);
}
/* Currently VirtualBox always uses locatime
* so locatime is always true here */
def->clock.offset = VIR_DOMAIN_CLOCK_OFFSET_LOCALTIME;
vboxDumpVideo(def, data, machine);
vboxDumpDisplay(def, data, machine);
/* As the medium interface changed from 3.0 to 3.1.
* There are two totally different implementations.
* The old one would be version specified, while the
* new one is using the vboxUniformedAPI and be put
* into the common code.
*/
if (gVBoxAPI.oldMediumInterface)
gVBoxAPI.dumpIDEHDDsOld(def, data, machine);
else
vboxDumpIDEHDDsNew(def, data, machine);
vboxDumpSharedFolders(def, data, machine);
vboxDumpNetwork(def, data, machine, networkAdapterCount);
vboxDumpAudio(def, data, machine);
if (gVBoxAPI.oldMediumInterface) {
gVBoxAPI.dumpDVD(def, data, machine);
gVBoxAPI.dumpFloppy(def, data, machine);
}
vboxDumpSerial(def, data, machine, serialPortCount);
vboxDumpParallel(def, data, machine, parallelPortCount);
/* dump USB devices/filters if active */
vboxHostDeviceGetXMLDesc(data, def, machine);
ret = virDomainDefFormat(def, flags);
cleanup:
VBOX_RELEASE(machine);
vboxIIDUnalloc(&iid);
virDomainDefFree(def);
return ret;
}
static int vboxConnectListDefinedDomains(virConnectPtr conn,
char ** const names, int maxnames)
{
VBOX_OBJECT_CHECK(conn, int, -1);
vboxArray machines = VBOX_ARRAY_INITIALIZER;
char *machineName = NULL;
PRUnichar *machineNameUtf16 = NULL;
PRUint32 state;
nsresult rc;
size_t i, j;
rc = gVBoxAPI.UArray.vboxArrayGet(&machines, data->vboxObj,
ARRAY_GET_MACHINES);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Could not get list of Defined Domains, rc=%08x"),
(unsigned)rc);
goto cleanup;
}
memset(names, 0, sizeof(names[i]) * maxnames);
ret = 0;
for (i = 0, j = 0; (i < machines.count) && (j < maxnames); i++) {
PRBool isAccessible = PR_FALSE;
IMachine *machine = machines.items[i];
if (!machine)
continue;
gVBoxAPI.UIMachine.GetAccessible(machine, &isAccessible);
if (!isAccessible)
continue;
gVBoxAPI.UIMachine.GetState(machine, &state);
if (!gVBoxAPI.machineStateChecker.Inactive(state))
continue;
gVBoxAPI.UIMachine.GetName(machine, &machineNameUtf16);
VBOX_UTF16_TO_UTF8(machineNameUtf16, &machineName);
if (VIR_STRDUP(names[j], machineName) < 0) {
VBOX_UTF16_FREE(machineNameUtf16);
VBOX_UTF8_FREE(machineName);
for (j = 0; j < maxnames; j++)
VIR_FREE(names[j]);
ret = -1;
goto cleanup;
}
VBOX_UTF16_FREE(machineNameUtf16);
VBOX_UTF8_FREE(machineName);
j++;
ret++;
}
cleanup:
gVBoxAPI.UArray.vboxArrayRelease(&machines);
return ret;
}
static int vboxConnectNumOfDefinedDomains(virConnectPtr conn)
{
VBOX_OBJECT_CHECK(conn, int, -1);
vboxArray machines = VBOX_ARRAY_INITIALIZER;
PRUint32 state;
nsresult rc;
size_t i;
rc = gVBoxAPI.UArray.vboxArrayGet(&machines, data->vboxObj,
ARRAY_GET_MACHINES);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Could not get number of Defined Domains, rc=%08x"),
(unsigned)rc);
goto cleanup;
}
ret = 0;
for (i = 0; i < machines.count; ++i) {
PRBool isAccessible = PR_FALSE;
IMachine *machine = machines.items[i];
if (!machine)
continue;
gVBoxAPI.UIMachine.GetAccessible(machine, &isAccessible);
if (!isAccessible)
continue;
gVBoxAPI.UIMachine.GetState(machine, &state);
if (gVBoxAPI.machineStateChecker.Inactive(state)) {
ret++;
}
}
cleanup:
gVBoxAPI.UArray.vboxArrayRelease(&machines);
return ret;
}
static int vboxDomainAttachDeviceImpl(virDomainPtr dom,
const char *xml,
int mediaChangeOnly ATTRIBUTE_UNUSED)
{
VBOX_OBJECT_CHECK(dom->conn, int, -1);
IMachine *machine = NULL;
vboxIIDUnion iid;
PRUint32 state;
virDomainDefPtr def = NULL;
virDomainDeviceDefPtr dev = NULL;
nsresult rc;
VBOX_IID_INITIALIZE(&iid);
if (VIR_ALLOC(def) < 0)
return ret;
if (VIR_STRDUP(def->os.type, "hvm") < 0)
goto cleanup;
dev = virDomainDeviceDefParse(xml, def, data->caps, data->xmlopt,
VIR_DOMAIN_XML_INACTIVE);
if (dev == NULL)
goto cleanup;
if (openSessionForMachine(data, dom->uuid, &iid, &machine, false) < 0)
goto cleanup;
if (!machine)
goto cleanup;
gVBoxAPI.UIMachine.GetState(machine, &state);
if (gVBoxAPI.machineStateChecker.Running(state) ||
gVBoxAPI.machineStateChecker.Paused(state)) {
rc = gVBoxAPI.UISession.OpenExisting(data, &iid, machine);
} else {
rc = gVBoxAPI.UISession.Open(data, &iid, machine);
}
if (NS_FAILED(rc))
goto cleanup;
rc = gVBoxAPI.UISession.GetMachine(data->vboxSession, &machine);
if (NS_SUCCEEDED(rc) && machine) {
/* ret = -VIR_ERR_ARGUMENT_UNSUPPORTED means the current device don't support hotplug. */
ret = -VIR_ERR_ARGUMENT_UNSUPPORTED;
if (dev->type == VIR_DOMAIN_DEVICE_DISK) {
if (gVBoxAPI.oldMediumInterface) {
const char *src = virDomainDiskGetSource(dev->data.disk);
int type = virDomainDiskGetType(dev->data.disk);
if (dev->data.disk->device == VIR_DOMAIN_DISK_DEVICE_CDROM) {
if (type == VIR_STORAGE_TYPE_FILE && src) {
ret = gVBoxAPI.attachDVD(data, machine, src);
} else if (type == VIR_STORAGE_TYPE_BLOCK) {
}
} else if (dev->data.disk->device == VIR_DOMAIN_DISK_DEVICE_FLOPPY) {
if (type == VIR_STORAGE_TYPE_FILE && src) {
ret = gVBoxAPI.attachFloppy(data, machine, src);
} else if (type == VIR_STORAGE_TYPE_BLOCK) {
}
}
}
} else if (dev->type == VIR_DOMAIN_DEVICE_NET) {
} else if (dev->type == VIR_DOMAIN_DEVICE_HOSTDEV) {
if (dev->data.hostdev->mode == VIR_DOMAIN_HOSTDEV_MODE_SUBSYS) {
if (dev->data.hostdev->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB) {
}
}
} else if (dev->type == VIR_DOMAIN_DEVICE_FS &&
dev->data.fs->type == VIR_DOMAIN_FS_TYPE_MOUNT) {
PRUnichar *nameUtf16;
PRUnichar *hostPathUtf16;
PRBool writable;
VBOX_UTF8_TO_UTF16(dev->data.fs->dst, &nameUtf16);
VBOX_UTF8_TO_UTF16(dev->data.fs->src, &hostPathUtf16);
writable = !dev->data.fs->readonly;
rc = gVBoxAPI.UIMachine.CreateSharedFolder(machine, nameUtf16, hostPathUtf16,
writable, PR_FALSE);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("could not attach shared folder '%s', rc=%08x"),
dev->data.fs->dst, (unsigned)rc);
ret = -1;
} else {
ret = 0;
}
VBOX_UTF16_FREE(nameUtf16);
VBOX_UTF16_FREE(hostPathUtf16);
}
gVBoxAPI.UIMachine.SaveSettings(machine);
VBOX_RELEASE(machine);
if (ret == -VIR_ERR_ARGUMENT_UNSUPPORTED) {
virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, _("Unsupported device type %d"), dev->type);
ret = -1;
}
}
gVBoxAPI.UISession.Close(data->vboxSession);
cleanup:
vboxIIDUnalloc(&iid);
virDomainDefFree(def);
virDomainDeviceDefFree(dev);
return ret;
}
static int vboxDomainAttachDevice(virDomainPtr dom, const char *xml)
{
return vboxDomainAttachDeviceImpl(dom, xml, 0);
}
static int vboxDomainAttachDeviceFlags(virDomainPtr dom, const char *xml,
unsigned int flags)
{
virCheckFlags(VIR_DOMAIN_AFFECT_LIVE, -1);
return vboxDomainAttachDeviceImpl(dom, xml, 0);
}
static int vboxDomainUpdateDeviceFlags(virDomainPtr dom, const char *xml,
unsigned int flags)
{
virCheckFlags(VIR_DOMAIN_AFFECT_CURRENT |
VIR_DOMAIN_AFFECT_LIVE |
VIR_DOMAIN_AFFECT_CONFIG, -1);
if (flags & VIR_DOMAIN_AFFECT_CONFIG) {
virReportError(VIR_ERR_OPERATION_INVALID, "%s",
_("cannot modify the persistent configuration of a domain"));
return -1;
}
return vboxDomainAttachDeviceImpl(dom, xml, 1);
}
static int vboxDomainDetachDevice(virDomainPtr dom, const char *xml)
{
VBOX_OBJECT_CHECK(dom->conn, int, -1);
IMachine *machine = NULL;
vboxIIDUnion iid;
PRUint32 state;
virDomainDefPtr def = NULL;
virDomainDeviceDefPtr dev = NULL;
nsresult rc;
VBOX_IID_INITIALIZE(&iid);
if (VIR_ALLOC(def) < 0)
return ret;
if (VIR_STRDUP(def->os.type, "hvm") < 0)
goto cleanup;
dev = virDomainDeviceDefParse(xml, def, data->caps, data->xmlopt,
VIR_DOMAIN_XML_INACTIVE);
if (dev == NULL)
goto cleanup;
if (openSessionForMachine(data, dom->uuid, &iid, &machine, false) < 0)
goto cleanup;
if (!machine)
goto cleanup;
gVBoxAPI.UIMachine.GetState(machine, &state);
if (gVBoxAPI.machineStateChecker.Running(state) ||
gVBoxAPI.machineStateChecker.Paused(state)) {
rc = gVBoxAPI.UISession.OpenExisting(data, &iid, machine);
} else {
rc = gVBoxAPI.UISession.Open(data, &iid, machine);
}
if (NS_FAILED(rc))
goto cleanup;
rc = gVBoxAPI.UISession.GetMachine(data->vboxSession, &machine);
if (NS_SUCCEEDED(rc) && machine) {
/* ret = -VIR_ERR_ARGUMENT_UNSUPPORTED means the current device don't support hotplug. */
ret = -VIR_ERR_ARGUMENT_UNSUPPORTED;
if (dev->type == VIR_DOMAIN_DEVICE_DISK) {
if (gVBoxAPI.oldMediumInterface) {
int type = virDomainDiskGetType(dev->data.disk);
if (dev->data.disk->device == VIR_DOMAIN_DISK_DEVICE_CDROM) {
if (type == VIR_STORAGE_TYPE_FILE) {
ret = gVBoxAPI.detachDVD(machine);
} else if (type == VIR_STORAGE_TYPE_BLOCK) {
}
} else if (dev->data.disk->device == VIR_DOMAIN_DISK_DEVICE_FLOPPY) {
if (type == VIR_STORAGE_TYPE_FILE) {
ret = gVBoxAPI.detachFloppy(machine);
} else if (type == VIR_STORAGE_TYPE_BLOCK) {
}
}
}
} else if (dev->type == VIR_DOMAIN_DEVICE_NET) {
} else if (dev->type == VIR_DOMAIN_DEVICE_HOSTDEV) {
if (dev->data.hostdev->mode == VIR_DOMAIN_HOSTDEV_MODE_SUBSYS) {
if (dev->data.hostdev->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB) {
}
}
} else if (dev->type == VIR_DOMAIN_DEVICE_FS &&
dev->data.fs->type == VIR_DOMAIN_FS_TYPE_MOUNT) {
PRUnichar *nameUtf16;
VBOX_UTF8_TO_UTF16(dev->data.fs->dst, &nameUtf16);
rc = gVBoxAPI.UIMachine.RemoveSharedFolder(machine, nameUtf16);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("could not detach shared folder '%s', rc=%08x"),
dev->data.fs->dst, (unsigned)rc);
} else {
ret = 0;
}
VBOX_UTF16_FREE(nameUtf16);
}
gVBoxAPI.UIMachine.SaveSettings(machine);
VBOX_RELEASE(machine);
if (ret == -VIR_ERR_ARGUMENT_UNSUPPORTED) {
virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, _("Unsupported device type %d"), dev->type);
ret = -1;
}
}
gVBoxAPI.UISession.Close(data->vboxSession);
cleanup:
vboxIIDUnalloc(&iid);
virDomainDefFree(def);
virDomainDeviceDefFree(dev);
return ret;
}
static int vboxDomainDetachDeviceFlags(virDomainPtr dom, const char *xml,
unsigned int flags)
{
virCheckFlags(VIR_DOMAIN_AFFECT_LIVE, -1);
return vboxDomainDetachDevice(dom, xml);
}
static int vboxCloseDisksRecursively(virDomainPtr dom, char *location)
{
VBOX_OBJECT_CHECK(dom->conn, int, -1);
nsresult rc;
size_t i = 0;
PRUnichar *locationUtf = NULL;
IMedium *medium = NULL;
IMedium **children = NULL;
PRUint32 childrenSize = 0;
if (!gVBoxAPI.vboxSnapshotRedefine)
VIR_WARN("This function may not work in current version");
VBOX_UTF8_TO_UTF16(location, &locationUtf);
rc = gVBoxAPI.UIVirtualBox.OpenMedium(data->vboxObj,
locationUtf,
DeviceType_HardDisk,
AccessMode_ReadWrite,
&medium);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Unable to open HardDisk, rc=%08x"),
(unsigned)rc);
goto cleanup;
}
rc = gVBoxAPI.UIMedium.GetChildren(medium, &childrenSize, &children);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s"
, _("Unable to get disk children"));
goto cleanup;
}
for (i = 0; i < childrenSize; i++) {
IMedium *childMedium = children[i];
if (childMedium) {
PRUnichar *childLocationUtf = NULL;
char *childLocation = NULL;
rc = gVBoxAPI.UIMedium.GetLocation(childMedium, &childLocationUtf);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Unable to get childMedium location"));
goto cleanup;
}
VBOX_UTF16_TO_UTF8(childLocationUtf, &childLocation);
VBOX_UTF16_FREE(childLocationUtf);
if (vboxCloseDisksRecursively(dom, childLocation) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s"
, _("Unable to close disk children"));
goto cleanup;
}
VIR_FREE(childLocation);
}
}
rc = gVBoxAPI.UIMedium.Close(medium);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Unable to close HardDisk, rc=%08x"),
(unsigned)rc);
goto cleanup;
}
ret = 0;
cleanup:
VBOX_UTF16_FREE(locationUtf);
return ret;
}
static int
vboxSnapshotRedefine(virDomainPtr dom,
virDomainSnapshotDefPtr def,
bool isCurrent)
{
/*
* If your snapshot has a parent,
* it will only be redefined if you have already
* redefined the parent.
*
* The general algorithm of this function is below :
* First of all, we are going to create our vboxSnapshotXmlMachinePtr struct from
* the machine settings path.
* Then, if the machine current snapshot xml file is saved in the machine location,
* it means that this snapshot was previously modified by us and has fake disks.
* Fake disks are added when the flag VIR_DOMAIN_SNAPSHOT_CREATE_CURRENT was not set
* yet, in order to not corrupt read-only disks. The first thing to do is to remove those
* disks and restore the read-write disks, if any, in the vboxSnapshotXmlMachinePtr struct.
* We also delete the current snapshot xml file.
*
* After that, we are going to register the snapshot read-only disks that we want to redefine,
* if they are not in the media registry struct.
*
* The next step is to unregister the machine and close all disks.
*
* Then, we check if the flag VIR_DOMAIN_SNAPSHOT_CREATE_REDEFINE has already been set.
* If this flag was set, we just add read-write disks to the media registry
* struct. Otherwise, we save the snapshot xml file into the machine location in order
* to recover the read-write disks during the next redefine and we create differential disks
* from the snapshot read-only disks and add them to the media registry struct.
*
* Finally, we register the machine with the new virtualbox description file.
*/
VBOX_OBJECT_CHECK(dom->conn, int, -1);
vboxIIDUnion domiid;
IMachine *machine = NULL;
nsresult rc;
PRUnichar *settingsFilePath = NULL;
char *settingsFilePath_Utf8 = NULL;
virVBoxSnapshotConfMachinePtr snapshotMachineDesc = NULL;
char *currentSnapshotXmlFilePath = NULL;
PRUnichar *machineNameUtf16 = NULL;
char *machineName = NULL;
char **realReadWriteDisksPath = NULL;
int realReadWriteDisksPathSize = 0;
char **realReadOnlyDisksPath = NULL;
int realReadOnlyDisksPathSize = 0;
virVBoxSnapshotConfSnapshotPtr newSnapshotPtr = NULL;
unsigned char snapshotUuid[VIR_UUID_BUFLEN];
int it = 0;
int jt = 0;
PRUint32 aMediaSize = 0;
IMedium **aMedia = NULL;
char *machineLocationPath = NULL;
char *nameTmpUse = NULL;
bool snapshotFileExists = false;
bool needToChangeStorageController = false;
if (!gVBoxAPI.vboxSnapshotRedefine)
VIR_WARN("This function may not work in current version");
if (openSessionForMachine(data, dom->uuid, &domiid, &machine, false) < 0)
goto cleanup;
rc = gVBoxAPI.UIMachine.SaveSettings(machine);
/*It may failed when the machine is not mutable.*/
rc = gVBoxAPI.UIMachine.GetSettingsFilePath(machine, &settingsFilePath);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("cannot get settings file path"));
goto cleanup;
}
VBOX_UTF16_TO_UTF8(settingsFilePath, &settingsFilePath_Utf8);
/*Getting the machine name to retrieve the machine location path.*/
rc = gVBoxAPI.UIMachine.GetName(machine, &machineNameUtf16);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("cannot get machine name"));
goto cleanup;
}
VBOX_UTF16_TO_UTF8(machineNameUtf16, &machineName);
if (virAsprintf(&nameTmpUse, "%s.vbox", machineName) < 0)
goto cleanup;
machineLocationPath = virStringReplace(settingsFilePath_Utf8, nameTmpUse, "");
if (machineLocationPath == NULL) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Unable to get the machine location path"));
goto cleanup;
}
/*We create the xml struct with the settings file path.*/
snapshotMachineDesc = virVBoxSnapshotConfLoadVboxFile(settingsFilePath_Utf8, machineLocationPath);
if (snapshotMachineDesc == NULL) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("cannot create a vboxSnapshotXmlPtr"));
goto cleanup;
}
if (snapshotMachineDesc->currentSnapshot != NULL) {
if (virAsprintf(&currentSnapshotXmlFilePath, "%s%s.xml", machineLocationPath,
snapshotMachineDesc->currentSnapshot) < 0)
goto cleanup;
snapshotFileExists = virFileExists(currentSnapshotXmlFilePath);
}
if (snapshotFileExists) {
/*
* We have created fake disks, so we have to remove them and replace them with
* the read-write disks if there are any. The fake disks will be closed during
* the machine unregistration.
*/
if (virVBoxSnapshotConfRemoveFakeDisks(snapshotMachineDesc) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Unable to remove Fake Disks"));
goto cleanup;
}
realReadWriteDisksPathSize = virVBoxSnapshotConfGetRWDisksPathsFromLibvirtXML(currentSnapshotXmlFilePath,
&realReadWriteDisksPath);
realReadOnlyDisksPathSize = virVBoxSnapshotConfGetRODisksPathsFromLibvirtXML(currentSnapshotXmlFilePath,
&realReadOnlyDisksPath);
/*The read-only disk number is necessarily greater or equal to the
*read-write disk number*/
if (realReadOnlyDisksPathSize < realReadWriteDisksPathSize) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("The read only disk number must be greater or equal to the "
" read write disk number"));
goto cleanup;
}
for (it = 0; it < realReadWriteDisksPathSize; it++) {
virVBoxSnapshotConfHardDiskPtr readWriteDisk = NULL;
PRUnichar *locationUtf = NULL;
IMedium *readWriteMedium = NULL;
char *uuid = NULL;
PRUnichar *formatUtf = NULL;
char *format = NULL;
const char *parentUuid = NULL;
vboxIIDUnion iid;
VBOX_IID_INITIALIZE(&iid);
VBOX_UTF8_TO_UTF16(realReadWriteDisksPath[it], &locationUtf);
rc = gVBoxAPI.UIVirtualBox.OpenMedium(data->vboxObj,
locationUtf,
DeviceType_HardDisk,
AccessMode_ReadWrite,
&readWriteMedium);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Unable to open HardDisk, rc=%08x"),
(unsigned)rc);
VBOX_UTF16_FREE(locationUtf);
goto cleanup;
}
VBOX_UTF16_FREE(locationUtf);
rc = gVBoxAPI.UIMedium.GetId(readWriteMedium, &iid);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Unable to get the read write medium id"));
goto cleanup;
}
gVBoxAPI.UIID.vboxIIDToUtf8(data, &iid, &uuid);
vboxIIDUnalloc(&iid);
rc = gVBoxAPI.UIMedium.GetFormat(readWriteMedium, &formatUtf);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Unable to get the read write medium format"));
goto cleanup;
}
VBOX_UTF16_TO_UTF8(formatUtf, &format);
VBOX_UTF16_FREE(formatUtf);
if (VIR_ALLOC(readWriteDisk) < 0) {
VIR_FREE(formatUtf);
goto cleanup;
}
readWriteDisk->format = format;
readWriteDisk->uuid = uuid;
readWriteDisk->location = realReadWriteDisksPath[it];
/*
* We get the current snapshot's read-only disk uuid in order to add the
* read-write disk to the media registry as it's child. The read-only disk
* is already in the media registry because it is the fake disk's parent.
*/
parentUuid = virVBoxSnapshotConfHardDiskUuidByLocation(snapshotMachineDesc,
realReadOnlyDisksPath[it]);
if (parentUuid == NULL) {
VIR_FREE(readWriteDisk);
goto cleanup;
}
if (virVBoxSnapshotConfAddHardDiskToMediaRegistry(readWriteDisk,
snapshotMachineDesc->mediaRegistry,
parentUuid) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Unable to add hard disk to media Registry"));
VIR_FREE(readWriteDisk);
goto cleanup;
}
rc = gVBoxAPI.UIMedium.Close(readWriteMedium);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Unable to close HardDisk, rc=%08x"),
(unsigned)rc);
goto cleanup;
}
}
/*
* Now we have done this swap, we remove the snapshot xml file from the
* current machine location.
*/
if (unlink(currentSnapshotXmlFilePath) < 0) {
virReportSystemError(errno,
_("Unable to delete file %s"), currentSnapshotXmlFilePath);
goto cleanup;
}
}
/*
* Before unregistering the machine, while all disks are still open, ensure that all
* read-only disks are in the redefined snapshot's media registry (the disks need to
* be open to query their uuid).
*/
for (it = 0; it < def->dom->ndisks; it++) {
int diskInMediaRegistry = 0;
IMedium *readOnlyMedium = NULL;
PRUnichar *locationUtf = NULL;
char *uuid = NULL;
PRUnichar *formatUtf = NULL;
char *format = NULL;
char *parentUuid = NULL;
virVBoxSnapshotConfHardDiskPtr readOnlyDisk = NULL;
vboxIIDUnion iid, parentiid;
VBOX_IID_INITIALIZE(&iid);
VBOX_IID_INITIALIZE(&parentiid);
diskInMediaRegistry = virVBoxSnapshotConfDiskIsInMediaRegistry(snapshotMachineDesc,
def->dom->disks[it]->src->path);
if (diskInMediaRegistry == -1) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Unable to know if disk is in media registry"));
goto cleanup;
}
if (diskInMediaRegistry == 1) /*Nothing to do.*/
continue;
/*The read only disk is not in the media registry*/
VBOX_UTF8_TO_UTF16(def->dom->disks[it]->src->path, &locationUtf);
rc = gVBoxAPI.UIVirtualBox.OpenMedium(data->vboxObj,
locationUtf,
DeviceType_HardDisk,
AccessMode_ReadWrite,
&readOnlyMedium);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Unable to open HardDisk, rc=%08x"),
(unsigned)rc);
VBOX_UTF16_FREE(locationUtf);
goto cleanup;
}
VBOX_UTF16_FREE(locationUtf);
rc = gVBoxAPI.UIMedium.GetId(readOnlyMedium, &iid);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Unable to get hard disk id"));
goto cleanup;
}
gVBoxAPI.UIID.vboxIIDToUtf8(data, &iid, &uuid);
vboxIIDUnalloc(&iid);
rc = gVBoxAPI.UIMedium.GetFormat(readOnlyMedium, &formatUtf);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Unable to get hard disk format"));
VIR_FREE(uuid);
goto cleanup;
}
VBOX_UTF16_TO_UTF8(formatUtf, &format);
VBOX_UTF16_FREE(formatUtf);
/*This disk is already in the media registry*/
IMedium *parentReadOnlyMedium = NULL;
rc = gVBoxAPI.UIMedium.GetParent(readOnlyMedium, &parentReadOnlyMedium);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Unable to get parent hard disk"));
VIR_FREE(uuid);
goto cleanup;
}
rc = gVBoxAPI.UIMedium.GetId(parentReadOnlyMedium, &parentiid);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Unable to get hard disk id, rc=%08x"),
(unsigned)rc);
VIR_FREE(uuid);
goto cleanup;
}
gVBoxAPI.UIID.vboxIIDToUtf8(data, &parentiid, &parentUuid);
vboxIIDUnalloc(&parentiid);
rc = gVBoxAPI.UIMedium.Close(readOnlyMedium);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Unable to close HardDisk, rc=%08x"),
(unsigned)rc);
VIR_FREE(uuid);
VIR_FREE(parentUuid);
goto cleanup;
}
if (VIR_ALLOC(readOnlyDisk) < 0) {
VIR_FREE(uuid);
VIR_FREE(parentUuid);
goto cleanup;
}
readOnlyDisk->format = format;
readOnlyDisk->uuid = uuid;
if (VIR_STRDUP(readOnlyDisk->location, def->dom->disks[it]->src->path) < 0) {
VIR_FREE(readOnlyDisk);
goto cleanup;
}
if (virVBoxSnapshotConfAddHardDiskToMediaRegistry(readOnlyDisk, snapshotMachineDesc->mediaRegistry,
parentUuid) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Unable to add hard disk to media registry"));
VIR_FREE(readOnlyDisk);
goto cleanup;
}
}
/*Now, we can unregister the machine*/
rc = gVBoxAPI.UIMachine.Unregister(machine,
CleanupMode_DetachAllReturnHardDisksOnly,
&aMediaSize,
&aMedia);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Unable to unregister machine, rc=%08x"),
(unsigned)rc);
goto cleanup;
}
VBOX_RELEASE(machine);
/*
* Unregister the machine, and then close all disks returned by the unregister method.
* Some close operations will fail because some disks that need to be closed will not
* be returned by virtualbox. We will close them just after. We have to use this
* solution because it is the only way to delete fake disks.
*/
for (it = 0; it < aMediaSize; it++) {
IMedium *medium = aMedia[it];
if (medium) {
PRUnichar *locationUtf16 = NULL;
char *locationUtf8 = NULL;
rc = gVBoxAPI.UIMedium.GetLocation(medium, &locationUtf16);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Unable to get medium location"));
goto cleanup;
}
VBOX_UTF16_TO_UTF8(locationUtf16, &locationUtf8);
VBOX_UTF16_FREE(locationUtf16);
if (strstr(locationUtf8, "fake") != NULL) {
/*we delete the fake disk because we don't need it anymore*/
IProgress *progress = NULL;
resultCodeUnion resultCode;
rc = gVBoxAPI.UIMedium.DeleteStorage(medium, &progress);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Unable to delete medium, rc=%08x"),
(unsigned)rc);
VIR_FREE(locationUtf8);
goto cleanup;
}
gVBoxAPI.UIProgress.WaitForCompletion(progress, -1);
gVBoxAPI.UIProgress.GetResultCode(progress, &resultCode);
if (RC_FAILED(resultCode)) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Error while closing medium, rc=%08x"),
resultCode.uResultCode);
VIR_FREE(locationUtf8);
goto cleanup;
}
VBOX_RELEASE(progress);
} else {
/*
* This a comment from vboxmanage code in the handleUnregisterVM
* function in VBoxManageMisc.cpp :
* Note that the IMachine::Unregister method will return the medium
* reference in a sane order, which means that closing will normally
* succeed, unless there is still another machine which uses the
* medium. No harm done if we ignore the error.
*/
ignore_value(gVBoxAPI.UIMedium.Close(medium));
}
VBOX_UTF8_FREE(locationUtf8);
}
}
/*Close all disks that failed to close normally.*/
for (it = 0; it < snapshotMachineDesc->mediaRegistry->ndisks; it++) {
if (vboxCloseDisksRecursively(dom, snapshotMachineDesc->mediaRegistry->disks[it]->location) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Unable to close recursively all disks"));
goto cleanup;
}
}
/*Here, all disks are closed or deleted*/
/*We are now going to create and fill the Snapshot xml struct*/
if (VIR_ALLOC(newSnapshotPtr) < 0)
goto cleanup;
if (virUUIDGenerate(snapshotUuid) < 0)
goto cleanup;
char uuidtmp[VIR_UUID_STRING_BUFLEN];
virUUIDFormat(snapshotUuid, uuidtmp);
if (VIR_STRDUP(newSnapshotPtr->uuid, uuidtmp) < 0)
goto cleanup;
VIR_DEBUG("New snapshot UUID: %s", newSnapshotPtr->uuid);
if (VIR_STRDUP(newSnapshotPtr->name, def->name) < 0)
goto cleanup;
newSnapshotPtr->timeStamp = virTimeStringThen(def->creationTime * 1000);
if (VIR_STRDUP(newSnapshotPtr->description, def->description) < 0)
goto cleanup;
if (VIR_STRDUP(newSnapshotPtr->hardware, snapshotMachineDesc->hardware) < 0)
goto cleanup;
if (VIR_STRDUP(newSnapshotPtr->storageController, snapshotMachineDesc->storageController) < 0)
goto cleanup;
/*We get the parent disk uuid from the parent disk location to correctly fill the storage controller.*/
for (it = 0; it < def->dom->ndisks; it++) {
char *location = NULL;
const char *uuidReplacing = NULL;
char **searchResultTab = NULL;
ssize_t resultSize = 0;
char *tmp = NULL;
location = def->dom->disks[it]->src->path;
if (!location)
goto cleanup;
/*Replacing the uuid*/
uuidReplacing = virVBoxSnapshotConfHardDiskUuidByLocation(snapshotMachineDesc, location);
if (uuidReplacing == NULL)
goto cleanup;
resultSize = virStringSearch(newSnapshotPtr->storageController,
VBOX_UUID_REGEX,
it + 1,
&searchResultTab);
if (resultSize != it + 1)
goto cleanup;
tmp = virStringReplace(newSnapshotPtr->storageController,
searchResultTab[it],
uuidReplacing);
virStringFreeList(searchResultTab);
VIR_FREE(newSnapshotPtr->storageController);
if (!tmp)
goto cleanup;
if (VIR_STRDUP(newSnapshotPtr->storageController, tmp) < 0)
goto cleanup;
VIR_FREE(tmp);
}
if (virVBoxSnapshotConfAddSnapshotToXmlMachine(newSnapshotPtr, snapshotMachineDesc, def->parent) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Unable to add the snapshot to the machine description"));
goto cleanup;
}
/*
* We change the current snapshot only if there is no current snapshot or if the
* snapshotFile exists, otherwise, it means that the correct current snapshot is
* already set.
*/
if (snapshotMachineDesc->currentSnapshot == NULL || snapshotFileExists) {
snapshotMachineDesc->currentSnapshot = newSnapshotPtr->uuid;
needToChangeStorageController = true;
}
/*
* Open the snapshot's read-write disk's full ancestry to allow opening the
* read-write disk itself.
*/
for (it = 0; it < def->dom->ndisks; it++) {
char *location = NULL;
virVBoxSnapshotConfHardDiskPtr *hardDiskToOpen = NULL;
size_t hardDiskToOpenSize = 0;
location = def->dom->disks[it]->src->path;
if (!location)
goto cleanup;
hardDiskToOpenSize = virVBoxSnapshotConfDiskListToOpen(snapshotMachineDesc,
&hardDiskToOpen, location);
for (jt = hardDiskToOpenSize -1; jt >= 0; jt--) {
IMedium *medium = NULL;
PRUnichar *locationUtf16 = NULL;
VBOX_UTF8_TO_UTF16(hardDiskToOpen[jt]->location, &locationUtf16);
rc = gVBoxAPI.UIVirtualBox.OpenMedium(data->vboxObj,
locationUtf16,
DeviceType_HardDisk,
AccessMode_ReadWrite,
&medium);
VBOX_UTF16_FREE(locationUtf16);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Unable to open HardDisk, rc=%08x"),
(unsigned)rc);
goto cleanup;
}
}
}
if (isCurrent || !needToChangeStorageController) {
/* We don't create a differential hard disk because either the current snapshot
* has already been defined or the snapshot to redefine is the current snapshot.
* If the snapshot to redefine is the current snapshot, we add read-write disks in
* the machine storage controllers.
*/
for (it = 0; it < def->ndisks; it++) {
IMedium *medium = NULL;
PRUnichar *locationUtf16 = NULL;
virVBoxSnapshotConfHardDiskPtr disk = NULL;
PRUnichar *formatUtf16 = NULL;
char *format = NULL;
char *uuid = NULL;
IMedium *parentDisk = NULL;
char *parentUuid = NULL;
vboxIIDUnion iid, parentiid;
VBOX_IID_INITIALIZE(&iid);
VBOX_IID_INITIALIZE(&parentiid);
VBOX_UTF8_TO_UTF16(def->disks[it].src->path, &locationUtf16);
rc = gVBoxAPI.UIVirtualBox.OpenMedium(data->vboxObj,
locationUtf16,
DeviceType_HardDisk,
AccessMode_ReadWrite,
&medium);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Unable to open HardDisk, rc=%08x"),
(unsigned)rc);
goto cleanup;
}
VBOX_UTF16_FREE(locationUtf16);
if (VIR_ALLOC(disk) < 0)
goto cleanup;
rc = gVBoxAPI.UIMedium.GetFormat(medium, &formatUtf16);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Unable to get disk format"));
VIR_FREE(disk);
goto cleanup;
}
VBOX_UTF16_TO_UTF8(formatUtf16, &format);
disk->format = format;
VBOX_UTF16_FREE(formatUtf16);
if (VIR_STRDUP(disk->location, def->disks[it].src->path) < 0) {
VIR_FREE(disk);
goto cleanup;
}
rc = gVBoxAPI.UIMedium.GetId(medium, &iid);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Unable to get disk uuid"));
VIR_FREE(disk);
goto cleanup;
}
gVBoxAPI.UIID.vboxIIDToUtf8(data, &iid, &uuid);
disk->uuid = uuid;
vboxIIDUnalloc(&iid);
rc = gVBoxAPI.UIMedium.GetParent(medium, &parentDisk);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Unable to get disk parent"));
VIR_FREE(disk);
goto cleanup;
}
gVBoxAPI.UIMedium.GetId(parentDisk, &parentiid);
gVBoxAPI.UIID.vboxIIDToUtf8(data, &parentiid, &parentUuid);
vboxIIDUnalloc(&parentiid);
if (virVBoxSnapshotConfAddHardDiskToMediaRegistry(disk,
snapshotMachineDesc->mediaRegistry,
parentUuid) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Unable to add hard disk to the media registry"));
VIR_FREE(disk);
goto cleanup;
}
if (needToChangeStorageController) {
/*We need to append this disk in the storage controller*/
char **searchResultTab = NULL;
ssize_t resultSize = 0;
char *tmp = NULL;
resultSize = virStringSearch(snapshotMachineDesc->storageController,
VBOX_UUID_REGEX,
it + 1,
&searchResultTab);
if (resultSize != it + 1) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Unable to find UUID %s"), searchResultTab[it]);
goto cleanup;
}
tmp = virStringReplace(snapshotMachineDesc->storageController,
searchResultTab[it],
disk->uuid);
virStringFreeList(searchResultTab);
VIR_FREE(snapshotMachineDesc->storageController);
if (!tmp)
goto cleanup;
if (VIR_STRDUP(snapshotMachineDesc->storageController, tmp) < 0)
goto cleanup;
VIR_FREE(tmp);
}
/*Close disk*/
rc = gVBoxAPI.UIMedium.Close(medium);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Unable to close HardDisk, rc=%08x"),
(unsigned)rc);
goto cleanup;
}
}
} else {
/*Create a "fake" disk to avoid corrupting children snapshot disks.*/
for (it = 0; it < def->dom->ndisks; it++) {
IMedium *medium = NULL;
PRUnichar *locationUtf16 = NULL;
char *parentUuid = NULL;
IMedium *newMedium = NULL;
PRUnichar *formatUtf16 = NULL;
PRUnichar *newLocation = NULL;
char *newLocationUtf8 = NULL;
resultCodeUnion resultCode;
virVBoxSnapshotConfHardDiskPtr disk = NULL;
char *uuid = NULL;
char *format = NULL;
char **searchResultTab = NULL;
ssize_t resultSize = 0;
char *tmp = NULL;
vboxIIDUnion iid, parentiid;
VBOX_IID_INITIALIZE(&iid);
VBOX_IID_INITIALIZE(&parentiid);
VBOX_UTF8_TO_UTF16(def->dom->disks[it]->src->path, &locationUtf16);
rc = gVBoxAPI.UIVirtualBox.OpenMedium(data->vboxObj,
locationUtf16,
DeviceType_HardDisk,
AccessMode_ReadWrite,
&medium);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Unable to open HardDisk, rc=%08x"),
(unsigned)rc);
VBOX_UTF16_FREE(locationUtf16);
goto cleanup;
}
VBOX_UTF16_FREE(locationUtf16);
rc = gVBoxAPI.UIMedium.GetId(medium, &parentiid);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Unable to get hardDisk Id, rc=%08x"),
(unsigned)rc);
goto cleanup;
}
gVBoxAPI.UIID.vboxIIDToUtf8(data, &parentiid, &parentUuid);
vboxIIDUnalloc(&parentiid);
VBOX_UTF8_TO_UTF16("VDI", &formatUtf16);
if (virAsprintf(&newLocationUtf8, "%sfakedisk-%d.vdi", machineLocationPath, it) < 0)
goto cleanup;
VBOX_UTF8_TO_UTF16(newLocationUtf8, &newLocation);
rc = gVBoxAPI.UIVirtualBox.CreateHardDiskMedium(data->vboxObj,
formatUtf16,
newLocation,
&newMedium);
VBOX_UTF16_FREE(newLocation);
VBOX_UTF16_FREE(formatUtf16);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Unable to create HardDisk, rc=%08x"),
(unsigned)rc);
goto cleanup;
}
IProgress *progress = NULL;
PRUint32 tab[1];
tab[0] = MediumVariant_Diff;
gVBoxAPI.UIMedium.CreateDiffStorage(medium, newMedium, 1, tab, &progress);
gVBoxAPI.UIProgress.WaitForCompletion(progress, -1);
gVBoxAPI.UIProgress.GetResultCode(progress, &resultCode);
if (RC_FAILED(resultCode)) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Error while creating diff storage, rc=%08x"),
resultCode.uResultCode);
goto cleanup;
}
VBOX_RELEASE(progress);
/*
* The differential disk is created, we add it to the media registry and the
* machine storage controllers.
*/
if (VIR_ALLOC(disk) < 0)
goto cleanup;
rc = gVBoxAPI.UIMedium.GetId(newMedium, &iid);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Unable to get medium uuid, rc=%08x"),
(unsigned)rc);
goto cleanup;
}
gVBoxAPI.UIID.vboxIIDToUtf8(data, &iid, &uuid);
disk->uuid = uuid;
vboxIIDUnalloc(&iid);
if (VIR_STRDUP(disk->location, newLocationUtf8) < 0)
goto cleanup;
rc = gVBoxAPI.UIMedium.GetFormat(newMedium, &formatUtf16);
VBOX_UTF16_TO_UTF8(formatUtf16, &format);
disk->format = format;
VBOX_UTF16_FREE(formatUtf16);
if (virVBoxSnapshotConfAddHardDiskToMediaRegistry(disk,
snapshotMachineDesc->mediaRegistry,
parentUuid) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Unable to add hard disk to the media registry"));
goto cleanup;
}
/*Adding the fake disk to the machine storage controllers*/
resultSize = virStringSearch(snapshotMachineDesc->storageController,
VBOX_UUID_REGEX,
it + 1,
&searchResultTab);
if (resultSize != it + 1) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Unable to find UUID %s"), searchResultTab[it]);
goto cleanup;
}
tmp = virStringReplace(snapshotMachineDesc->storageController,
searchResultTab[it],
disk->uuid);
virStringFreeList(searchResultTab);
VIR_FREE(snapshotMachineDesc->storageController);
if (!tmp)
goto cleanup;
if (VIR_STRDUP(snapshotMachineDesc->storageController, tmp) < 0)
goto cleanup;
VIR_FREE(tmp);
/*Closing the "fake" disk*/
rc = gVBoxAPI.UIMedium.Close(newMedium);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Unable to close the new medium, rc=%08x"),
(unsigned)rc);
goto cleanup;
}
}
/*
* We save the snapshot xml file to retrieve the real read-write disk during the
* next define. This file is saved as "'machineLocation'/snapshot-'uuid'.xml"
*/
VIR_FREE(currentSnapshotXmlFilePath);
if (virAsprintf(&currentSnapshotXmlFilePath, "%s%s.xml", machineLocationPath, snapshotMachineDesc->currentSnapshot) < 0)
goto cleanup;
char *snapshotContent = virDomainSnapshotDefFormat(NULL, def, VIR_DOMAIN_XML_SECURE, 0);
if (snapshotContent == NULL) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Unable to get snapshot content"));
goto cleanup;
}
if (virFileWriteStr(currentSnapshotXmlFilePath, snapshotContent, 0644) < 0) {
virReportSystemError(errno, "%s",
_("Unable to save new snapshot xml file"));
goto cleanup;
}
VIR_FREE(snapshotContent);
}
/*
* All the snapshot structure manipulation is done, we close the disks we have
* previously opened.
*/
for (it = 0; it < def->dom->ndisks; it++) {
char *location = def->dom->disks[it]->src->path;
if (!location)
goto cleanup;
virVBoxSnapshotConfHardDiskPtr *hardDiskToOpen = NULL;
size_t hardDiskToOpenSize = virVBoxSnapshotConfDiskListToOpen(snapshotMachineDesc,
&hardDiskToOpen, location);
for (jt = 0; jt < hardDiskToOpenSize; jt++) {
IMedium *medium = NULL;
PRUnichar *locationUtf16 = NULL;
VBOX_UTF8_TO_UTF16(hardDiskToOpen[jt]->location, &locationUtf16);
rc = gVBoxAPI.UIVirtualBox.OpenMedium(data->vboxObj,
locationUtf16,
DeviceType_HardDisk,
AccessMode_ReadWrite,
&medium);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Unable to open HardDisk, rc=%08x"),
(unsigned)rc);
goto cleanup;
}
rc = gVBoxAPI.UIMedium.Close(medium);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Unable to close HardDisk, rc=%08x"),
(unsigned)rc);
goto cleanup;
}
VBOX_UTF16_FREE(locationUtf16);
}
}
/*Now, we rewrite the 'machineName'.vbox file to redefine the machine.*/
if (virVBoxSnapshotConfSaveVboxFile(snapshotMachineDesc, settingsFilePath_Utf8) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Unable to serialize the machine description"));
goto cleanup;
}
rc = gVBoxAPI.UIVirtualBox.OpenMachine(data->vboxObj,
settingsFilePath,
&machine);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Unable to open Machine, rc=%08x"),
(unsigned)rc);
goto cleanup;
}
rc = gVBoxAPI.UIVirtualBox.RegisterMachine(data->vboxObj, machine);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Unable to register Machine, rc=%08x"),
(unsigned)rc);
goto cleanup;
}
ret = 0;
cleanup:
VBOX_RELEASE(machine);
VBOX_UTF16_FREE(settingsFilePath);
VBOX_UTF8_FREE(settingsFilePath_Utf8);
VIR_FREE(snapshotMachineDesc);
VIR_FREE(currentSnapshotXmlFilePath);
VBOX_UTF16_FREE(machineNameUtf16);
VBOX_UTF8_FREE(machineName);
virStringFreeList(realReadOnlyDisksPath);
virStringFreeList(realReadWriteDisksPath);
VIR_FREE(newSnapshotPtr);
VIR_FREE(machineLocationPath);
VIR_FREE(nameTmpUse);
return ret;
}
static virDomainSnapshotPtr
vboxDomainSnapshotCreateXML(virDomainPtr dom,
const char *xmlDesc,
unsigned int flags)
{
VBOX_OBJECT_CHECK(dom->conn, virDomainSnapshotPtr, NULL);
virDomainSnapshotDefPtr def = NULL;
vboxIIDUnion domiid;
IMachine *machine = NULL;
IConsole *console = NULL;
IProgress *progress = NULL;
ISnapshot *snapshot = NULL;
PRUnichar *name = NULL;
PRUnichar *description = NULL;
PRUint32 state;
nsresult rc;
resultCodeUnion result;
VBOX_IID_INITIALIZE(&domiid);
/* VBox has no snapshot metadata, so this flag is trivial. */
virCheckFlags(VIR_DOMAIN_SNAPSHOT_CREATE_NO_METADATA |
VIR_DOMAIN_SNAPSHOT_CREATE_REDEFINE |
VIR_DOMAIN_SNAPSHOT_CREATE_CURRENT, NULL);
if (!(def = virDomainSnapshotDefParseString(xmlDesc, data->caps,
data->xmlopt, -1,
VIR_DOMAIN_SNAPSHOT_PARSE_DISKS |
VIR_DOMAIN_SNAPSHOT_PARSE_REDEFINE)))
goto cleanup;
if (openSessionForMachine(data, dom->uuid, &domiid, &machine, false) < 0)
goto cleanup;
if (gVBoxAPI.vboxSnapshotRedefine) {
PRBool isCurrent = flags & VIR_DOMAIN_SNAPSHOT_CREATE_CURRENT;
if (flags & VIR_DOMAIN_SNAPSHOT_CREATE_REDEFINE) {
if (vboxSnapshotRedefine(dom, def, isCurrent) < 0)
goto cleanup;
ret = virGetDomainSnapshot(dom, def->name);
goto cleanup;
}
}
rc = gVBoxAPI.UIMachine.GetState(machine, &state);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("could not get domain state"));
goto cleanup;
}
if (gVBoxAPI.machineStateChecker.Online(state)) {
rc = gVBoxAPI.UISession.OpenExisting(data, &domiid, machine);
} else {
rc = gVBoxAPI.UISession.Open(data, &domiid, machine);
}
if (NS_SUCCEEDED(rc))
rc = gVBoxAPI.UISession.GetConsole(data->vboxSession, &console);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("could not open VirtualBox session with domain %s"),
dom->name);
goto cleanup;
}
VBOX_UTF8_TO_UTF16(def->name, &name);
if (!name) {
virReportOOMError();
goto cleanup;
}
if (def->description) {
VBOX_UTF8_TO_UTF16(def->description, &description);
if (!description) {
virReportOOMError();
goto cleanup;
}
}
rc = gVBoxAPI.UIConsole.TakeSnapshot(console, name, description, &progress);
if (NS_FAILED(rc) || !progress) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("could not take snapshot of domain %s"), dom->name);
goto cleanup;
}
gVBoxAPI.UIProgress.WaitForCompletion(progress, -1);
gVBoxAPI.UIProgress.GetResultCode(progress, &result);
if (RC_FAILED(result)) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("could not take snapshot of domain %s"), dom->name);
goto cleanup;
}
rc = gVBoxAPI.UIMachine.GetCurrentSnapshot(machine, &snapshot);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("could not get current snapshot of domain %s"),
dom->name);
goto cleanup;
}
ret = virGetDomainSnapshot(dom, def->name);
cleanup:
VBOX_RELEASE(progress);
VBOX_UTF16_FREE(description);
VBOX_UTF16_FREE(name);
VBOX_RELEASE(console);
gVBoxAPI.UISession.Close(data->vboxSession);
VBOX_RELEASE(machine);
vboxIIDUnalloc(&domiid);
virDomainSnapshotDefFree(def);
return ret;
}
static int
vboxDomainSnapshotGetAll(virDomainPtr dom,
IMachine *machine,
ISnapshot ***snapshots)
{
vboxIIDUnion empty;
ISnapshot **list = NULL;
PRUint32 count;
nsresult rc;
unsigned int next;
unsigned int top;
VBOX_IID_INITIALIZE(&empty);
rc = gVBoxAPI.UIMachine.GetSnapshotCount(machine, &count);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("could not get snapshot count for domain %s"),
dom->name);
goto error;
}
if (count == 0)
goto out;
if (VIR_ALLOC_N(list, count) < 0)
goto error;
rc = gVBoxAPI.UIMachine.FindSnapshot(machine, &empty, list);
if (NS_FAILED(rc) || !list[0]) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("could not get root snapshot for domain %s"),
dom->name);
goto error;
}
/* BFS walk through snapshot tree */
top = 1;
for (next = 0; next < count; next++) {
vboxArray children = VBOX_ARRAY_INITIALIZER;
size_t i;
if (!list[next]) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("unexpected number of snapshots < %u"), count);
goto error;
}
rc = gVBoxAPI.UArray.vboxArrayGet(&children, list[next],
gVBoxAPI.UArray.handleSnapshotGetChildren(list[next]));
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR,
"%s", _("could not get children snapshots"));
goto error;
}
for (i = 0; i < children.count; i++) {
ISnapshot *child = children.items[i];
if (!child)
continue;
if (top == count) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("unexpected number of snapshots > %u"), count);
gVBoxAPI.UArray.vboxArrayRelease(&children);
goto error;
}
VBOX_ADDREF(child);
list[top++] = child;
}
gVBoxAPI.UArray.vboxArrayRelease(&children);
}
out:
*snapshots = list;
return count;
error:
if (list) {
for (next = 0; next < count; next++)
VBOX_RELEASE(list[next]);
}
VIR_FREE(list);
return -1;
}
static ISnapshot *
vboxDomainSnapshotGet(vboxGlobalData *data,
virDomainPtr dom,
IMachine *machine,
const char *name)
{
ISnapshot **snapshots = NULL;
ISnapshot *snapshot = NULL;
nsresult rc;
int count = 0;
size_t i;
if ((count = vboxDomainSnapshotGetAll(dom, machine, &snapshots)) < 0)
goto cleanup;
for (i = 0; i < count; i++) {
PRUnichar *nameUtf16;
char *nameUtf8;
rc = gVBoxAPI.UISnapshot.GetName(snapshots[i], &nameUtf16);
if (NS_FAILED(rc) || !nameUtf16) {
virReportError(VIR_ERR_INTERNAL_ERROR,
"%s", _("could not get snapshot name"));
goto cleanup;
}
VBOX_UTF16_TO_UTF8(nameUtf16, &nameUtf8);
VBOX_UTF16_FREE(nameUtf16);
if (STREQ(name, nameUtf8))
snapshot = snapshots[i];
VBOX_UTF8_FREE(nameUtf8);
if (snapshot)
break;
}
if (!snapshot) {
virReportError(VIR_ERR_OPERATION_INVALID,
_("domain %s has no snapshots with name %s"),
dom->name, name);
goto cleanup;
}
cleanup:
if (count > 0) {
for (i = 0; i < count; i++) {
if (snapshots[i] != snapshot)
VBOX_RELEASE(snapshots[i]);
}
}
VIR_FREE(snapshots);
return snapshot;
}
static int vboxSnapshotGetReadWriteDisks(virDomainSnapshotDefPtr def,
virDomainSnapshotPtr snapshot)
{
virDomainPtr dom = snapshot->domain;
VBOX_OBJECT_CHECK(dom->conn, int, -1);
vboxIIDUnion domiid;
IMachine *machine = NULL;
ISnapshot *snap = NULL;
IMachine *snapMachine = NULL;
vboxArray mediumAttachments = VBOX_ARRAY_INITIALIZER;
PRUint32 maxPortPerInst[StorageBus_Floppy + 1] = {};
PRUint32 maxSlotPerPort[StorageBus_Floppy + 1] = {};
int diskCount = 0;
nsresult rc;
vboxIIDUnion snapIid;
char *snapshotUuidStr = NULL;
size_t i = 0;
if (!gVBoxAPI.vboxSnapshotRedefine)
VIR_WARN("This function may not work in current version");
VBOX_IID_INITIALIZE(&snapIid);
if (openSessionForMachine(data, dom->uuid, &domiid, &machine, false) < 0)
goto cleanup;
if (!(snap = vboxDomainSnapshotGet(data, dom, machine, snapshot->name)))
goto cleanup;
rc = gVBoxAPI.UISnapshot.GetId(snap, &snapIid);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Could not get snapshot id"));
goto cleanup;
}
gVBoxAPI.UIID.vboxIIDToUtf8(data, &snapIid, &snapshotUuidStr);
vboxIIDUnalloc(&snapIid);
rc = gVBoxAPI.UISnapshot.GetMachine(snap, &snapMachine);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("could not get machine"));
goto cleanup;
}
def->ndisks = 0;
rc = gVBoxAPI.UArray.vboxArrayGet(&mediumAttachments, snapMachine,
gVBoxAPI.UArray.handleMachineGetMediumAttachments(snapMachine));
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("no medium attachments"));
goto cleanup;
}
/* get the number of attachments */
for (i = 0; i < mediumAttachments.count; i++) {
IMediumAttachment *imediumattach = mediumAttachments.items[i];
if (imediumattach) {
IMedium *medium = NULL;
rc = gVBoxAPI.UIMediumAttachment.GetMedium(imediumattach, &medium);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("cannot get medium"));
goto cleanup;
}
if (medium) {
def->ndisks++;
VBOX_RELEASE(medium);
}
}
}
/* Allocate mem, if fails return error */
if (VIR_ALLOC_N(def->disks, def->ndisks) < 0)
goto cleanup;
for (i = 0; i < def->ndisks; i++) {
if (VIR_ALLOC(def->disks[i].src) < 0)
goto cleanup;
}
if (!vboxGetMaxPortSlotValues(data->vboxObj, maxPortPerInst, maxSlotPerPort))
goto cleanup;
/* get the attachment details here */
for (i = 0; i < mediumAttachments.count && diskCount < def->ndisks; i++) {
IStorageController *storageController = NULL;
PRUnichar *storageControllerName = NULL;
PRUint32 deviceType = DeviceType_Null;
PRUint32 storageBus = StorageBus_Null;
IMedium *disk = NULL;
PRUnichar *childLocUtf16 = NULL;
char *childLocUtf8 = NULL;
PRUint32 deviceInst = 0;
PRInt32 devicePort = 0;
PRInt32 deviceSlot = 0;
vboxArray children = VBOX_ARRAY_INITIALIZER;
vboxArray snapshotIids = VBOX_ARRAY_INITIALIZER;
IMediumAttachment *imediumattach = mediumAttachments.items[i];
void *handle;
size_t j = 0;
size_t k = 0;
if (!imediumattach)
continue;
rc = gVBoxAPI.UIMediumAttachment.GetMedium(imediumattach, &disk);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("cannot get medium"));
goto cleanup;
}
if (!disk)
continue;
rc = gVBoxAPI.UIMediumAttachment.GetController(imediumattach, &storageControllerName);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("cannot get controller"));
goto cleanup;
}
if (!storageControllerName) {
VBOX_RELEASE(disk);
continue;
}
handle = gVBoxAPI.UArray.handleMediumGetChildren(disk);
rc = gVBoxAPI.UArray.vboxArrayGet(&children, disk, handle);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("cannot get children disk"));
goto cleanup;
}
handle = gVBoxAPI.UArray.handleMediumGetSnapshotIds(disk);
rc = gVBoxAPI.UArray.vboxArrayGetWithIIDArg(&snapshotIids, disk,
handle, &domiid);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("cannot get snapshot ids"));
goto cleanup;
}
for (j = 0; j < children.count; ++j) {
IMedium *child = children.items[j];
for (k = 0; k < snapshotIids.count; ++k) {
PRUnichar *diskSnapId = snapshotIids.items[k];
char *diskSnapIdStr = NULL;
VBOX_UTF16_TO_UTF8(diskSnapId, &diskSnapIdStr);
if (STREQ(diskSnapIdStr, snapshotUuidStr)) {
rc = gVBoxAPI.UIMachine.GetStorageControllerByName(machine,
storageControllerName,
&storageController);
VBOX_UTF16_FREE(storageControllerName);
if (!storageController) {
VBOX_RELEASE(child);
break;
}
rc = gVBoxAPI.UIMedium.GetLocation(child, &childLocUtf16);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("cannot get disk location"));
goto cleanup;
}
VBOX_UTF16_TO_UTF8(childLocUtf16, &childLocUtf8);
VBOX_UTF16_FREE(childLocUtf16);
if (VIR_STRDUP(def->disks[diskCount].src->path, childLocUtf8) < 0) {
VBOX_RELEASE(child);
VBOX_RELEASE(storageController);
goto cleanup;
}
VBOX_UTF8_FREE(childLocUtf8);
rc = gVBoxAPI.UIStorageController.GetBus(storageController, &storageBus);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("cannot get storage controller bus"));
goto cleanup;
}
rc = gVBoxAPI.UIMediumAttachment.GetType(imediumattach, &deviceType);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("cannot get medium attachment type"));
goto cleanup;
}
rc = gVBoxAPI.UIMediumAttachment.GetPort(imediumattach, &devicePort);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("cannot get medium attachment type"));
goto cleanup;
}
rc = gVBoxAPI.UIMediumAttachment.GetDevice(imediumattach, &deviceSlot);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("cannot get medium attachment device"));
goto cleanup;
}
def->disks[diskCount].src->type = VIR_STORAGE_TYPE_FILE;
def->disks[diskCount].name = vboxGenerateMediumName(storageBus,
deviceInst,
devicePort,
deviceSlot,
maxPortPerInst,
maxSlotPerPort);
}
VBOX_UTF8_FREE(diskSnapIdStr);
}
}
VBOX_RELEASE(storageController);
VBOX_RELEASE(disk);
diskCount++;
}
gVBoxAPI.UArray.vboxArrayRelease(&mediumAttachments);
ret = 0;
cleanup:
if (ret < 0) {
for (i = 0; i < def->ndisks; i++) {
VIR_FREE(def->disks[i].src);
}
VIR_FREE(def->disks);
def->ndisks = 0;
}
VBOX_RELEASE(snap);
return ret;
}
static
int vboxSnapshotGetReadOnlyDisks(virDomainSnapshotPtr snapshot,
virDomainSnapshotDefPtr def)
{
virDomainPtr dom = snapshot->domain;
VBOX_OBJECT_CHECK(dom->conn, int, -1);
vboxIIDUnion domiid;
ISnapshot *snap = NULL;
IMachine *machine = NULL;
IMachine *snapMachine = NULL;
IStorageController *storageController = NULL;
IMedium *disk = NULL;
nsresult rc;
vboxArray mediumAttachments = VBOX_ARRAY_INITIALIZER;
size_t i = 0;
PRUint32 maxPortPerInst[StorageBus_Floppy + 1] = {};
PRUint32 maxSlotPerPort[StorageBus_Floppy + 1] = {};
int diskCount = 0;
if (!gVBoxAPI.vboxSnapshotRedefine)
VIR_WARN("This function may not work in current version");
if (openSessionForMachine(data, dom->uuid, &domiid, &machine, false) < 0)
goto cleanup;
if (!(snap = vboxDomainSnapshotGet(data, dom, machine, snapshot->name)))
goto cleanup;
rc = gVBoxAPI.UISnapshot.GetMachine(snap, &snapMachine);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("cannot get machine"));
goto cleanup;
}
/*
* Get READ ONLY disks
* In the snapshot metadata, these are the disks written inside the <domain> node
*/
rc = gVBoxAPI.UArray.vboxArrayGet(&mediumAttachments, snapMachine,
gVBoxAPI.UArray.handleMachineGetMediumAttachments(snapMachine));
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("cannot get medium attachments"));
goto cleanup;
}
/* get the number of attachments */
for (i = 0; i < mediumAttachments.count; i++) {
IMediumAttachment *imediumattach = mediumAttachments.items[i];
if (imediumattach) {
IMedium *medium = NULL;
rc = gVBoxAPI.UIMediumAttachment.GetMedium(imediumattach, &medium);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("cannot get medium"));
goto cleanup;
}
if (medium) {
def->dom->ndisks++;
VBOX_RELEASE(medium);
}
}
}
/* Allocate mem, if fails return error */
if (VIR_ALLOC_N(def->dom->disks, def->dom->ndisks) >= 0) {
for (i = 0; i < def->dom->ndisks; i++) {
virDomainDiskDefPtr diskDef = virDomainDiskDefNew();
if (!diskDef)
goto cleanup;
def->dom->disks[i] = diskDef;
}
} else {
goto cleanup;
}
if (!vboxGetMaxPortSlotValues(data->vboxObj, maxPortPerInst, maxSlotPerPort))
goto cleanup;
/* get the attachment details here */
for (i = 0; i < mediumAttachments.count && diskCount < def->dom->ndisks; i++) {
PRUnichar *storageControllerName = NULL;
PRUint32 deviceType = DeviceType_Null;
PRUint32 storageBus = StorageBus_Null;
PRBool readOnly = PR_FALSE;
PRUnichar *mediumLocUtf16 = NULL;
char *mediumLocUtf8 = NULL;
PRUint32 deviceInst = 0;
PRInt32 devicePort = 0;
PRInt32 deviceSlot = 0;
IMediumAttachment *imediumattach = mediumAttachments.items[i];
if (!imediumattach)
continue;
rc = gVBoxAPI.UIMediumAttachment.GetMedium(imediumattach, &disk);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("cannot get medium"));
goto cleanup;
}
if (!disk)
continue;
rc = gVBoxAPI.UIMediumAttachment.GetController(imediumattach, &storageControllerName);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("cannot get storage controller name"));
goto cleanup;
}
if (!storageControllerName)
continue;
rc = gVBoxAPI.UIMachine.GetStorageControllerByName(machine,
storageControllerName,
&storageController);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("cannot get storage controller"));
goto cleanup;
}
VBOX_UTF16_FREE(storageControllerName);
if (!storageController)
continue;
rc = gVBoxAPI.UIMedium.GetLocation(disk, &mediumLocUtf16);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("cannot get disk location"));
goto cleanup;
}
VBOX_UTF16_TO_UTF8(mediumLocUtf16, &mediumLocUtf8);
VBOX_UTF16_FREE(mediumLocUtf16);
if (VIR_STRDUP(def->dom->disks[diskCount]->src->path, mediumLocUtf8) < 0)
goto cleanup;
VBOX_UTF8_FREE(mediumLocUtf8);
rc = gVBoxAPI.UIStorageController.GetBus(storageController, &storageBus);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("cannot get storage controller bus"));
goto cleanup;
}
if (storageBus == StorageBus_IDE) {
def->dom->disks[diskCount]->bus = VIR_DOMAIN_DISK_BUS_IDE;
} else if (storageBus == StorageBus_SATA) {
def->dom->disks[diskCount]->bus = VIR_DOMAIN_DISK_BUS_SATA;
} else if (storageBus == StorageBus_SCSI) {
def->dom->disks[diskCount]->bus = VIR_DOMAIN_DISK_BUS_SCSI;
} else if (storageBus == StorageBus_Floppy) {
def->dom->disks[diskCount]->bus = VIR_DOMAIN_DISK_BUS_FDC;
}
rc = gVBoxAPI.UIMediumAttachment.GetType(imediumattach, &deviceType);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("cannot get medium attachment type"));
goto cleanup;
}
if (deviceType == DeviceType_HardDisk)
def->dom->disks[diskCount]->device = VIR_DOMAIN_DISK_DEVICE_DISK;
else if (deviceType == DeviceType_Floppy)
def->dom->disks[diskCount]->device = VIR_DOMAIN_DISK_DEVICE_FLOPPY;
else if (deviceType == DeviceType_DVD)
def->dom->disks[diskCount]->device = VIR_DOMAIN_DISK_DEVICE_CDROM;
rc = gVBoxAPI.UIMediumAttachment.GetPort(imediumattach, &devicePort);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("cannot get medium attachment port"));
goto cleanup;
}
rc = gVBoxAPI.UIMediumAttachment.GetDevice(imediumattach, &deviceSlot);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("cannot get device"));
goto cleanup;
}
rc = gVBoxAPI.UIMedium.GetReadOnly(disk, &readOnly);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("cannot get read only attribute"));
goto cleanup;
}
if (readOnly == PR_TRUE)
def->dom->disks[diskCount]->src->readonly = true;
def->dom->disks[diskCount]->src->type = VIR_STORAGE_TYPE_FILE;
def->dom->disks[diskCount]->dst = vboxGenerateMediumName(storageBus,
deviceInst,
devicePort,
deviceSlot,
maxPortPerInst,
maxSlotPerPort);
if (!def->dom->disks[diskCount]->dst) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Could not generate medium name for the disk "
"at: controller instance:%u, port:%d, slot:%d"),
deviceInst, devicePort, deviceSlot);
ret = -1;
goto cleanup;
}
diskCount ++;
}
/* cleanup on error */
ret = 0;
cleanup:
if (ret < 0) {
for (i = 0; i < def->dom->ndisks; i++)
virDomainDiskDefFree(def->dom->disks[i]);
VIR_FREE(def->dom->disks);
def->dom->ndisks = 0;
}
VBOX_RELEASE(disk);
VBOX_RELEASE(storageController);
gVBoxAPI.UArray.vboxArrayRelease(&mediumAttachments);
VBOX_RELEASE(snap);
return ret;
}
static char *vboxDomainSnapshotGetXMLDesc(virDomainSnapshotPtr snapshot,
unsigned int flags)
{
virDomainPtr dom = snapshot->domain;
VBOX_OBJECT_CHECK(dom->conn, char *, NULL);
vboxIIDUnion domiid;
IMachine *machine = NULL;
ISnapshot *snap = NULL;
ISnapshot *parent = NULL;
nsresult rc;
virDomainSnapshotDefPtr def = NULL;
PRUnichar *str16;
char *str8;
PRInt64 timestamp;
PRBool online = PR_FALSE;
char uuidstr[VIR_UUID_STRING_BUFLEN];
virCheckFlags(0, NULL);
if (openSessionForMachine(data, dom->uuid, &domiid, &machine, false) < 0)
goto cleanup;
if (!(snap = vboxDomainSnapshotGet(data, dom, machine, snapshot->name)))
goto cleanup;
if (VIR_ALLOC(def) < 0 || VIR_ALLOC(def->dom) < 0)
goto cleanup;
if (VIR_STRDUP(def->name, snapshot->name) < 0)
goto cleanup;
if (gVBoxAPI.vboxSnapshotRedefine) {
/* Register def->dom properties for them to be saved inside the snapshot XMl
* Otherwise, there is a problem while parsing the xml
*/
PRUint32 memorySize = 0;
PRUint32 CPUCount = 0;
def->dom->virtType = VIR_DOMAIN_VIRT_VBOX;
def->dom->id = dom->id;
memcpy(def->dom->uuid, dom->uuid, VIR_UUID_BUFLEN);
if (VIR_STRDUP(def->dom->name, dom->name) < 0)
goto cleanup;
gVBoxAPI.UIMachine.GetMemorySize(machine, &memorySize);
def->dom->mem.cur_balloon = memorySize * 1024;
/* Currently setting memory and maxMemory as same, cause
* the notation here seems to be inconsistent while
* reading and while dumping xml
*/
def->dom->mem.max_balloon = memorySize * 1024;
if (VIR_STRDUP(def->dom->os.type, "hvm") < 0)
goto cleanup;
def->dom->os.arch = virArchFromHost();
gVBoxAPI.UIMachine.GetCPUCount(machine, &CPUCount);
def->dom->maxvcpus = def->dom->vcpus = CPUCount;
if (vboxSnapshotGetReadWriteDisks(def, snapshot) < 0) {
VIR_DEBUG("Could not get read write disks for snapshot");
}
if (vboxSnapshotGetReadOnlyDisks(snapshot, def) < 0) {
VIR_DEBUG("Could not get Readonly disks for snapshot");
}
}
rc = gVBoxAPI.UISnapshot.GetDescription(snap, &str16);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("could not get description of snapshot %s"),
snapshot->name);
goto cleanup;
}
if (str16) {
VBOX_UTF16_TO_UTF8(str16, &str8);
VBOX_UTF16_FREE(str16);
if (VIR_STRDUP(def->description, str8) < 0) {
VBOX_UTF8_FREE(str8);
goto cleanup;
}
VBOX_UTF8_FREE(str8);
}
rc = gVBoxAPI.UISnapshot.GetTimeStamp(snap, &timestamp);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("could not get creation time of snapshot %s"),
snapshot->name);
goto cleanup;
}
/* timestamp is in milliseconds while creationTime in seconds */
def->creationTime = timestamp / 1000;
rc = gVBoxAPI.UISnapshot.GetParent(snap, &parent);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("could not get parent of snapshot %s"),
snapshot->name);
goto cleanup;
}
if (parent) {
rc = gVBoxAPI.UISnapshot.GetName(parent, &str16);
if (NS_FAILED(rc) || !str16) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("could not get name of parent of snapshot %s"),
snapshot->name);
goto cleanup;
}
VBOX_UTF16_TO_UTF8(str16, &str8);
VBOX_UTF16_FREE(str16);
if (VIR_STRDUP(def->parent, str8) < 0) {
VBOX_UTF8_FREE(str8);
goto cleanup;
}
VBOX_UTF8_FREE(str8);
}
rc = gVBoxAPI.UISnapshot.GetOnline(snap, &online);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("could not get online state of snapshot %s"),
snapshot->name);
goto cleanup;
}
if (online)
def->state = VIR_DOMAIN_RUNNING;
else
def->state = VIR_DOMAIN_SHUTOFF;
virUUIDFormat(dom->uuid, uuidstr);
memcpy(def->dom->uuid, dom->uuid, VIR_UUID_BUFLEN);
ret = virDomainSnapshotDefFormat(uuidstr, def, flags, 0);
cleanup:
virDomainSnapshotDefFree(def);
VBOX_RELEASE(parent);
VBOX_RELEASE(snap);
VBOX_RELEASE(machine);
vboxIIDUnalloc(&domiid);
return ret;
}
static int vboxDomainSnapshotNum(virDomainPtr dom, unsigned int flags)
{
VBOX_OBJECT_CHECK(dom->conn, int, -1);
vboxIIDUnion iid;
IMachine *machine = NULL;
nsresult rc;
PRUint32 snapshotCount;
virCheckFlags(VIR_DOMAIN_SNAPSHOT_LIST_ROOTS |
VIR_DOMAIN_SNAPSHOT_LIST_METADATA, -1);
if (openSessionForMachine(data, dom->uuid, &iid, &machine, false) < 0)
goto cleanup;
/* VBox snapshots do not require libvirt to maintain any metadata. */
if (flags & VIR_DOMAIN_SNAPSHOT_LIST_METADATA) {
ret = 0;
goto cleanup;
}
rc = gVBoxAPI.UIMachine.GetSnapshotCount(machine, &snapshotCount);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("could not get snapshot count for domain %s"),
dom->name);
goto cleanup;
}
/* VBox has at most one root snapshot. */
if (snapshotCount && (flags & VIR_DOMAIN_SNAPSHOT_LIST_ROOTS))
ret = 1;
else
ret = snapshotCount;
cleanup:
VBOX_RELEASE(machine);
vboxIIDUnalloc(&iid);
return ret;
}
static int vboxDomainSnapshotListNames(virDomainPtr dom, char **names,
int nameslen, unsigned int flags)
{
VBOX_OBJECT_CHECK(dom->conn, int, -1);
vboxIIDUnion iid;
IMachine *machine = NULL;
nsresult rc;
ISnapshot **snapshots = NULL;
int count = 0;
size_t i;
virCheckFlags(VIR_DOMAIN_SNAPSHOT_LIST_ROOTS |
VIR_DOMAIN_SNAPSHOT_LIST_METADATA, -1);
if (openSessionForMachine(data, dom->uuid, &iid, &machine, false) < 0)
goto cleanup;
if (flags & VIR_DOMAIN_SNAPSHOT_LIST_METADATA) {
ret = 0;
goto cleanup;
}
if (flags & VIR_DOMAIN_SNAPSHOT_LIST_ROOTS) {
vboxIIDUnion empty;
VBOX_IID_INITIALIZE(&empty);
if (VIR_ALLOC_N(snapshots, 1) < 0)
goto cleanup;
rc = gVBoxAPI.UIMachine.FindSnapshot(machine, &empty, snapshots);
if (NS_FAILED(rc) || !snapshots[0]) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("could not get root snapshot for domain %s"),
dom->name);
goto cleanup;
}
count = 1;
} else {
if ((count = vboxDomainSnapshotGetAll(dom, machine, &snapshots)) < 0)
goto cleanup;
}
for (i = 0; i < nameslen; i++) {
PRUnichar *nameUtf16;
char *name;
if (i >= count)
break;
rc = gVBoxAPI.UISnapshot.GetName(snapshots[i], &nameUtf16);
if (NS_FAILED(rc) || !nameUtf16) {
virReportError(VIR_ERR_INTERNAL_ERROR,
"%s", _("could not get snapshot name"));
goto cleanup;
}
VBOX_UTF16_TO_UTF8(nameUtf16, &name);
VBOX_UTF16_FREE(nameUtf16);
if (VIR_STRDUP(names[i], name) < 0) {
VBOX_UTF8_FREE(name);
goto cleanup;
}
VBOX_UTF8_FREE(name);
}
if (count <= nameslen)
ret = count;
else
ret = nameslen;
cleanup:
if (count > 0) {
for (i = 0; i < count; i++)
VBOX_RELEASE(snapshots[i]);
}
VIR_FREE(snapshots);
VBOX_RELEASE(machine);
vboxIIDUnalloc(&iid);
return ret;
}
static virDomainSnapshotPtr
vboxDomainSnapshotLookupByName(virDomainPtr dom, const char *name,
unsigned int flags)
{
VBOX_OBJECT_CHECK(dom->conn, virDomainSnapshotPtr, NULL);
vboxIIDUnion iid;
IMachine *machine = NULL;
ISnapshot *snapshot = NULL;
virCheckFlags(0, NULL);
if (openSessionForMachine(data, dom->uuid, &iid, &machine, false) < 0)
goto cleanup;
if (!(snapshot = vboxDomainSnapshotGet(data, dom, machine, name)))
goto cleanup;
ret = virGetDomainSnapshot(dom, name);
cleanup:
VBOX_RELEASE(snapshot);
VBOX_RELEASE(machine);
vboxIIDUnalloc(&iid);
return ret;
}
static int vboxDomainHasCurrentSnapshot(virDomainPtr dom,
unsigned int flags)
{
VBOX_OBJECT_CHECK(dom->conn, int, -1);
vboxIIDUnion iid;
IMachine *machine = NULL;
ISnapshot *snapshot = NULL;
nsresult rc;
virCheckFlags(0, -1);
if (openSessionForMachine(data, dom->uuid, &iid, &machine, false) < 0)
goto cleanup;
rc = gVBoxAPI.UIMachine.GetCurrentSnapshot(machine, &snapshot);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("could not get current snapshot"));
goto cleanup;
}
if (snapshot)
ret = 1;
else
ret = 0;
cleanup:
VBOX_RELEASE(machine);
vboxIIDUnalloc(&iid);
return ret;
}
static virDomainSnapshotPtr
vboxDomainSnapshotGetParent(virDomainSnapshotPtr snapshot,
unsigned int flags)
{
virDomainPtr dom = snapshot->domain;
VBOX_OBJECT_CHECK(dom->conn, virDomainSnapshotPtr, NULL);
vboxIIDUnion iid;
IMachine *machine = NULL;
ISnapshot *snap = NULL;
ISnapshot *parent = NULL;
PRUnichar *nameUtf16 = NULL;
char *name = NULL;
nsresult rc;
virCheckFlags(0, NULL);
if (openSessionForMachine(data, dom->uuid, &iid, &machine, false) < 0)
goto cleanup;
if (!(snap = vboxDomainSnapshotGet(data, dom, machine, snapshot->name)))
goto cleanup;
rc = gVBoxAPI.UISnapshot.GetParent(snap, &parent);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("could not get parent of snapshot %s"),
snapshot->name);
goto cleanup;
}
if (!parent) {
virReportError(VIR_ERR_NO_DOMAIN_SNAPSHOT,
_("snapshot '%s' does not have a parent"),
snapshot->name);
goto cleanup;
}
rc = gVBoxAPI.UISnapshot.GetName(parent, &nameUtf16);
if (NS_FAILED(rc) || !nameUtf16) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("could not get name of parent of snapshot %s"),
snapshot->name);
goto cleanup;
}
VBOX_UTF16_TO_UTF8(nameUtf16, &name);
if (!name) {
virReportOOMError();
goto cleanup;
}
ret = virGetDomainSnapshot(dom, name);
cleanup:
VBOX_UTF8_FREE(name);
VBOX_UTF16_FREE(nameUtf16);
VBOX_RELEASE(snap);
VBOX_RELEASE(parent);
VBOX_RELEASE(machine);
vboxIIDUnalloc(&iid);
return ret;
}
static virDomainSnapshotPtr
vboxDomainSnapshotCurrent(virDomainPtr dom, unsigned int flags)
{
VBOX_OBJECT_CHECK(dom->conn, virDomainSnapshotPtr, NULL);
vboxIIDUnion iid;
IMachine *machine = NULL;
ISnapshot *snapshot = NULL;
PRUnichar *nameUtf16 = NULL;
char *name = NULL;
nsresult rc;
virCheckFlags(0, NULL);
if (openSessionForMachine(data, dom->uuid, &iid, &machine, false) < 0)
goto cleanup;
rc = gVBoxAPI.UIMachine.GetCurrentSnapshot(machine, &snapshot);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("could not get current snapshot"));
goto cleanup;
}
if (!snapshot) {
virReportError(VIR_ERR_OPERATION_INVALID, "%s",
_("domain has no snapshots"));
goto cleanup;
}
rc = gVBoxAPI.UISnapshot.GetName(snapshot, &nameUtf16);
if (NS_FAILED(rc) || !nameUtf16) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("could not get current snapshot name"));
goto cleanup;
}
VBOX_UTF16_TO_UTF8(nameUtf16, &name);
if (!name) {
virReportOOMError();
goto cleanup;
}
ret = virGetDomainSnapshot(dom, name);
cleanup:
VBOX_UTF8_FREE(name);
VBOX_UTF16_FREE(nameUtf16);
VBOX_RELEASE(snapshot);
VBOX_RELEASE(machine);
vboxIIDUnalloc(&iid);
return ret;
}
static int vboxDomainSnapshotIsCurrent(virDomainSnapshotPtr snapshot,
unsigned int flags)
{
virDomainPtr dom = snapshot->domain;
VBOX_OBJECT_CHECK(dom->conn, int, -1);
vboxIIDUnion iid;
IMachine *machine = NULL;
ISnapshot *snap = NULL;
ISnapshot *current = NULL;
PRUnichar *nameUtf16 = NULL;
char *name = NULL;
nsresult rc;
virCheckFlags(0, -1);
if (openSessionForMachine(data, dom->uuid, &iid, &machine, false) < 0)
goto cleanup;
if (!(snap = vboxDomainSnapshotGet(data, dom, machine, snapshot->name)))
goto cleanup;
rc = gVBoxAPI.UIMachine.GetCurrentSnapshot(machine, &current);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("could not get current snapshot"));
goto cleanup;
}
if (!current) {
ret = 0;
goto cleanup;
}
rc = gVBoxAPI.UISnapshot.GetName(current, &nameUtf16);
if (NS_FAILED(rc) || !nameUtf16) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("could not get current snapshot name"));
goto cleanup;
}
VBOX_UTF16_TO_UTF8(nameUtf16, &name);
if (!name) {
virReportOOMError();
goto cleanup;
}
ret = STREQ(snapshot->name, name);
cleanup:
VBOX_UTF8_FREE(name);
VBOX_UTF16_FREE(nameUtf16);
VBOX_RELEASE(snap);
VBOX_RELEASE(current);
VBOX_RELEASE(machine);
vboxIIDUnalloc(&iid);
return ret;
}
static int vboxDomainSnapshotHasMetadata(virDomainSnapshotPtr snapshot,
unsigned int flags)
{
virDomainPtr dom = snapshot->domain;
VBOX_OBJECT_CHECK(dom->conn, int, -1);
vboxIIDUnion iid;
IMachine *machine = NULL;
ISnapshot *snap = NULL;
virCheckFlags(0, -1);
if (openSessionForMachine(data, dom->uuid, &iid, &machine, false) < 0)
goto cleanup;
/* Check that snapshot exists. If so, there is no metadata. */
if (!(snap = vboxDomainSnapshotGet(data, dom, machine, snapshot->name)))
goto cleanup;
ret = 0;
cleanup:
VBOX_RELEASE(snap);
VBOX_RELEASE(machine);
vboxIIDUnalloc(&iid);
return ret;
}
static int vboxDomainRevertToSnapshot(virDomainSnapshotPtr snapshot,
unsigned int flags)
{
virDomainPtr dom = snapshot->domain;
VBOX_OBJECT_CHECK(dom->conn, int, -1);
vboxIIDUnion domiid;
IMachine *machine = NULL;
ISnapshot *newSnapshot = NULL;
ISnapshot *prevSnapshot = NULL;
PRBool online = PR_FALSE;
PRUint32 state;
nsresult rc;
virCheckFlags(0, -1);
if (openSessionForMachine(data, dom->uuid, &domiid, &machine, false) < 0)
goto cleanup;
newSnapshot = vboxDomainSnapshotGet(data, dom, machine, snapshot->name);
if (!newSnapshot)
goto cleanup;
rc = gVBoxAPI.UISnapshot.GetOnline(newSnapshot, &online);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("could not get online state of snapshot %s"),
snapshot->name);
goto cleanup;
}
rc = gVBoxAPI.UIMachine.GetCurrentSnapshot(machine, &prevSnapshot);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("could not get current snapshot of domain %s"),
dom->name);
goto cleanup;
}
rc = gVBoxAPI.UIMachine.GetState(machine, &state);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("could not get domain state"));
goto cleanup;
}
if (gVBoxAPI.machineStateChecker.Online(state)) {
virReportError(VIR_ERR_OPERATION_INVALID, "%s",
_("cannot revert snapshot of running domain"));
goto cleanup;
}
if (gVBoxAPI.snapshotRestore(dom, machine, newSnapshot))
goto cleanup;
if (online) {
ret = vboxDomainCreate(dom);
if (!ret)
gVBoxAPI.snapshotRestore(dom, machine, prevSnapshot);
} else {
ret = 0;
}
cleanup:
VBOX_RELEASE(prevSnapshot);
VBOX_RELEASE(newSnapshot);
vboxIIDUnalloc(&domiid);
return ret;
}
static int
vboxDomainSnapshotDeleteSingle(vboxGlobalData *data,
IConsole *console,
ISnapshot *snapshot)
{
IProgress *progress = NULL;
vboxIIDUnion iid;
int ret = -1;
nsresult rc;
resultCodeUnion result;
VBOX_IID_INITIALIZE(&iid);
rc = gVBoxAPI.UISnapshot.GetId(snapshot, &iid);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("could not get snapshot UUID"));
goto cleanup;
}
rc = gVBoxAPI.UIConsole.DeleteSnapshot(console, &iid, &progress);
if (NS_FAILED(rc) || !progress) {
if (rc == VBOX_E_INVALID_VM_STATE) {
virReportError(VIR_ERR_OPERATION_INVALID, "%s",
_("cannot delete domain snapshot for running domain"));
} else {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("could not delete snapshot"));
}
goto cleanup;
}
gVBoxAPI.UIProgress.WaitForCompletion(progress, -1);
gVBoxAPI.UIProgress.GetResultCode(progress, &result);
if (RC_FAILED(result)) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("could not delete snapshot"));
goto cleanup;
}
ret = 0;
cleanup:
VBOX_RELEASE(progress);
vboxIIDUnalloc(&iid);
return ret;
}
static int
vboxDomainSnapshotDeleteTree(vboxGlobalData *data,
IConsole *console,
ISnapshot *snapshot)
{
vboxArray children = VBOX_ARRAY_INITIALIZER;
int ret = -1;
nsresult rc;
size_t i;
rc = gVBoxAPI.UArray.vboxArrayGet(&children, snapshot,
gVBoxAPI.UArray.handleSnapshotGetChildren(snapshot));
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("could not get children snapshots"));
goto cleanup;
}
for (i = 0; i < children.count; i++) {
if (vboxDomainSnapshotDeleteTree(data, console, children.items[i]))
goto cleanup;
}
ret = vboxDomainSnapshotDeleteSingle(data, console, snapshot);
cleanup:
gVBoxAPI.UArray.vboxArrayRelease(&children);
return ret;
}
static int
vboxDomainSnapshotDeleteMetadataOnly(virDomainSnapshotPtr snapshot)
{
/*
* This function will remove the node in the vbox xml corresponding to the snapshot.
* It is usually called by vboxDomainSnapshotDelete() with the flag
* VIR_DOMAIN_SNAPSHOT_DELETE_METADATA_ONLY.
* If you want to use it anywhere else, be careful, if the snapshot you want to delete
* has children, the result is not granted, they will probably will be deleted in the
* xml, but you may have a problem with hard drives.
*
* If the snapshot which is being deleted is the current one, we will set the current
* snapshot of the machine to the parent of this snapshot. Before writing the modified
* xml file, we undefine the machine from vbox. After writing the file, we redefine
* the machine with the new file.
*/
virDomainPtr dom = snapshot->domain;
VBOX_OBJECT_CHECK(dom->conn, int, -1);
virDomainSnapshotDefPtr def = NULL;
char *defXml = NULL;
vboxIIDUnion domiid;
nsresult rc;
IMachine *machine = NULL;
PRUnichar *settingsFilePathUtf16 = NULL;
char *settingsFilepath = NULL;
virVBoxSnapshotConfMachinePtr snapshotMachineDesc = NULL;
int isCurrent = -1;
int it = 0;
PRUnichar *machineNameUtf16 = NULL;
char *machineName = NULL;
char *nameTmpUse = NULL;
char *machineLocationPath = NULL;
PRUint32 aMediaSize = 0;
IMedium **aMedia = NULL;
VBOX_IID_INITIALIZE(&domiid);
if (!gVBoxAPI.vboxSnapshotRedefine)
VIR_WARN("This function may not work in current version");
defXml = vboxDomainSnapshotGetXMLDesc(snapshot, 0);
if (!defXml) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Unable to get XML Desc of snapshot"));
goto cleanup;
}
def = virDomainSnapshotDefParseString(defXml,
data->caps,
data->xmlopt,
-1,
VIR_DOMAIN_SNAPSHOT_PARSE_DISKS |
VIR_DOMAIN_SNAPSHOT_PARSE_REDEFINE);
if (!def) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Unable to get a virDomainSnapshotDefPtr"));
goto cleanup;
}
if (openSessionForMachine(data, dom->uuid, &domiid, &machine, false) < 0)
goto cleanup;
rc = gVBoxAPI.UIMachine.GetSettingsFilePath(machine, &settingsFilePathUtf16);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("cannot get settings file path"));
goto cleanup;
}
VBOX_UTF16_TO_UTF8(settingsFilePathUtf16, &settingsFilepath);
/*Getting the machine name to retrieve the machine location path.*/
rc = gVBoxAPI.UIMachine.GetName(machine, &machineNameUtf16);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("cannot get machine name"));
goto cleanup;
}
VBOX_UTF16_TO_UTF8(machineNameUtf16, &machineName);
if (virAsprintf(&nameTmpUse, "%s.vbox", machineName) < 0)
goto cleanup;
machineLocationPath = virStringReplace(settingsFilepath, nameTmpUse, "");
if (machineLocationPath == NULL) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Unable to get the machine location path"));
goto cleanup;
}
snapshotMachineDesc = virVBoxSnapshotConfLoadVboxFile(settingsFilepath, machineLocationPath);
if (!snapshotMachineDesc) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("cannot create a vboxSnapshotXmlPtr"));
goto cleanup;
}
isCurrent = virVBoxSnapshotConfIsCurrentSnapshot(snapshotMachineDesc, def->name);
if (isCurrent < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Unable to know if the snapshot is the current snapshot"));
goto cleanup;
}
if (isCurrent) {
/*
* If the snapshot is the current snapshot, it means that the machine has read-write
* disks. The first thing to do is to manipulate VirtualBox API to create
* differential read-write disks if the parent snapshot is not null.
*/
if (def->parent != NULL) {
for (it = 0; it < def->dom->ndisks; it++) {
virVBoxSnapshotConfHardDiskPtr readOnly = NULL;
IMedium *medium = NULL;
PRUnichar *locationUtf16 = NULL;
char *parentUuid = NULL;
IMedium *newMedium = NULL;
PRUnichar *formatUtf16 = NULL;
PRUnichar *newLocation = NULL;
char *newLocationUtf8 = NULL;
IProgress *progress = NULL;
virVBoxSnapshotConfHardDiskPtr disk = NULL;
char *uuid = NULL;
char *format = NULL;
char **searchResultTab = NULL;
ssize_t resultSize = 0;
char *tmp = NULL;
vboxIIDUnion iid, parentiid;
resultCodeUnion resultCode;
VBOX_IID_INITIALIZE(&iid);
VBOX_IID_INITIALIZE(&parentiid);
readOnly = virVBoxSnapshotConfHardDiskPtrByLocation(snapshotMachineDesc,
def->dom->disks[it]->src->path);
if (!readOnly) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Cannot get hard disk by location"));
goto cleanup;
}
if (readOnly->parent == NULL) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("The read only disk has no parent"));
goto cleanup;
}
VBOX_UTF8_TO_UTF16(readOnly->parent->location, &locationUtf16);
rc = gVBoxAPI.UIVirtualBox.OpenMedium(data->vboxObj,
locationUtf16,
DeviceType_HardDisk,
AccessMode_ReadWrite,
&medium);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Unable to open HardDisk, rc=%08x"),
(unsigned)rc);
goto cleanup;
}
rc = gVBoxAPI.UIMedium.GetId(medium, &parentiid);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Unable to get hardDisk Id, rc=%08x"),
(unsigned)rc);
goto cleanup;
}
gVBoxAPI.UIID.vboxIIDToUtf8(data, &parentiid, &parentUuid);
vboxIIDUnalloc(&parentiid);
VBOX_UTF16_FREE(locationUtf16);
VBOX_UTF8_TO_UTF16("VDI", &formatUtf16);
if (virAsprintf(&newLocationUtf8, "%sfakedisk-%s-%d.vdi",
machineLocationPath, def->parent, it) < 0)
goto cleanup;
VBOX_UTF8_TO_UTF16(newLocationUtf8, &newLocation);
rc = gVBoxAPI.UIVirtualBox.CreateHardDiskMedium(data->vboxObj,
formatUtf16,
newLocation,
&newMedium);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Unable to create HardDisk, rc=%08x"),
(unsigned)rc);
goto cleanup;
}
VBOX_UTF16_FREE(formatUtf16);
VBOX_UTF16_FREE(newLocation);
PRUint32 tab[1];
tab[0] = MediumVariant_Diff;
gVBoxAPI.UIMedium.CreateDiffStorage(medium, newMedium, 1, tab, &progress);
gVBoxAPI.UIProgress.WaitForCompletion(progress, -1);
gVBoxAPI.UIProgress.GetResultCode(progress, &resultCode);
if (RC_FAILED(resultCode)) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Error while creating diff storage, rc=%08x"),
resultCode.uResultCode);
goto cleanup;
}
VBOX_RELEASE(progress);
/*
* The differential disk is created, we add it to the media registry and
* the machine storage controller.
*/
if (VIR_ALLOC(disk) < 0)
goto cleanup;
rc = gVBoxAPI.UIMedium.GetId(newMedium, &iid);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Unable to get medium uuid, rc=%08x"),
(unsigned)rc);
VIR_FREE(disk);
goto cleanup;
}
gVBoxAPI.UIID.vboxIIDToUtf8(data, &iid, &uuid);
disk->uuid = uuid;
vboxIIDUnalloc(&iid);
if (VIR_STRDUP(disk->location, newLocationUtf8) < 0) {
VIR_FREE(disk);
goto cleanup;
}
rc = gVBoxAPI.UIMedium.GetFormat(newMedium, &formatUtf16);
VBOX_UTF16_TO_UTF8(formatUtf16, &format);
disk->format = format;
VBOX_UTF16_FREE(formatUtf16);
if (virVBoxSnapshotConfAddHardDiskToMediaRegistry(disk,
snapshotMachineDesc->mediaRegistry,
parentUuid) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Unable to add hard disk to the media registry"));
goto cleanup;
}
/*Adding fake disks to the machine storage controllers*/
resultSize = virStringSearch(snapshotMachineDesc->storageController,
VBOX_UUID_REGEX,
it + 1,
&searchResultTab);
if (resultSize != it + 1) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Unable to find UUID %s"), searchResultTab[it]);
goto cleanup;
}
tmp = virStringReplace(snapshotMachineDesc->storageController,
searchResultTab[it],
disk->uuid);
virStringFreeList(searchResultTab);
VIR_FREE(snapshotMachineDesc->storageController);
if (!tmp)
goto cleanup;
if (VIR_STRDUP(snapshotMachineDesc->storageController, tmp) < 0)
goto cleanup;
VIR_FREE(tmp);
/*Closing the "fake" disk*/
rc = gVBoxAPI.UIMedium.Close(newMedium);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Unable to close the new medium, rc=%08x"),
(unsigned)rc);
goto cleanup;
}
}
} else {
for (it = 0; it < def->dom->ndisks; it++) {
const char *uuidRO = NULL;
char **searchResultTab = NULL;
ssize_t resultSize = 0;
char *tmp = NULL;
uuidRO = virVBoxSnapshotConfHardDiskUuidByLocation(snapshotMachineDesc,
def->dom->disks[it]->src->path);
if (!uuidRO) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("No such disk in media registry %s"),
def->dom->disks[it]->src->path);
goto cleanup;
}
resultSize = virStringSearch(snapshotMachineDesc->storageController,
VBOX_UUID_REGEX,
it + 1,
&searchResultTab);
if (resultSize != it + 1) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Unable to find UUID %s"),
searchResultTab[it]);
goto cleanup;
}
tmp = virStringReplace(snapshotMachineDesc->storageController,
searchResultTab[it],
uuidRO);
virStringFreeList(searchResultTab);
VIR_FREE(snapshotMachineDesc->storageController);
if (!tmp)
goto cleanup;
if (VIR_STRDUP(snapshotMachineDesc->storageController, tmp) < 0)
goto cleanup;
VIR_FREE(tmp);
}
}
}
/*We remove the read write disks from the media registry*/
for (it = 0; it < def->ndisks; it++) {
const char *uuidRW =
virVBoxSnapshotConfHardDiskUuidByLocation(snapshotMachineDesc,
def->disks[it].src->path);
if (!uuidRW) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Unable to find UUID for location %s"), def->disks[it].src->path);
goto cleanup;
}
if (virVBoxSnapshotConfRemoveHardDisk(snapshotMachineDesc->mediaRegistry, uuidRW) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Unable to remove disk from media registry. uuid = %s"), uuidRW);
goto cleanup;
}
}
/*If the parent snapshot is not NULL, we remove the-read only disks from the media registry*/
if (def->parent != NULL) {
for (it = 0; it < def->dom->ndisks; it++) {
const char *uuidRO =
virVBoxSnapshotConfHardDiskUuidByLocation(snapshotMachineDesc,
def->dom->disks[it]->src->path);
if (!uuidRO) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Unable to find UUID for location %s"), def->dom->disks[it]->src->path);
goto cleanup;
}
if (virVBoxSnapshotConfRemoveHardDisk(snapshotMachineDesc->mediaRegistry, uuidRO) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Unable to remove disk from media registry. uuid = %s"), uuidRO);
goto cleanup;
}
}
}
rc = gVBoxAPI.UIMachine.Unregister(machine,
CleanupMode_DetachAllReturnHardDisksOnly,
&aMediaSize,
&aMedia);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Unable to unregister machine, rc=%08x"),
(unsigned)rc);
goto cleanup;
}
VBOX_RELEASE(machine);
for (it = 0; it < aMediaSize; it++) {
IMedium *medium = aMedia[it];
PRUnichar *locationUtf16 = NULL;
char *locationUtf8 = NULL;
if (!medium)
continue;
rc = gVBoxAPI.UIMedium.GetLocation(medium, &locationUtf16);
VBOX_UTF16_TO_UTF8(locationUtf16, &locationUtf8);
if (isCurrent && strstr(locationUtf8, "fake") != NULL) {
/*we delete the fake disk because we don't need it anymore*/
IProgress *progress = NULL;
resultCodeUnion resultCode;
rc = gVBoxAPI.UIMedium.DeleteStorage(medium, &progress);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Unable to delete medium, rc=%08x"),
(unsigned)rc);
goto cleanup;
}
gVBoxAPI.UIProgress.WaitForCompletion(progress, -1);
gVBoxAPI.UIProgress.GetResultCode(progress, &resultCode);
if (RC_FAILED(resultCode)) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Error while closing medium, rc=%08x"),
resultCode.uResultCode);
goto cleanup;
}
VBOX_RELEASE(progress);
} else {
/* This a comment from vboxmanage code in the handleUnregisterVM
* function in VBoxManageMisc.cpp :
* Note that the IMachine::Unregister method will return the medium
* reference in a sane order, which means that closing will normally
* succeed, unless there is still another machine which uses the
* medium. No harm done if we ignore the error. */
ignore_value(gVBoxAPI.UIMedium.Close(medium));
}
VBOX_UTF16_FREE(locationUtf16);
VBOX_UTF8_FREE(locationUtf8);
}
/*removing the snapshot*/
if (virVBoxSnapshotConfRemoveSnapshot(snapshotMachineDesc, def->name) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Unable to remove snapshot %s"), def->name);
goto cleanup;
}
if (isCurrent) {
VIR_FREE(snapshotMachineDesc->currentSnapshot);
if (def->parent != NULL) {
virVBoxSnapshotConfSnapshotPtr snap = virVBoxSnapshotConfSnapshotByName(snapshotMachineDesc->snapshot, def->parent);
if (!snap) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Unable to get the snapshot to remove"));
goto cleanup;
}
if (VIR_STRDUP(snapshotMachineDesc->currentSnapshot, snap->uuid) < 0)
goto cleanup;
}
}
/*Registering the machine*/
if (virVBoxSnapshotConfSaveVboxFile(snapshotMachineDesc, settingsFilepath) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Unable to serialize the machine description"));
goto cleanup;
}
rc = gVBoxAPI.UIVirtualBox.OpenMachine(data->vboxObj,
settingsFilePathUtf16,
&machine);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Unable to open Machine, rc=%08x"),
(unsigned)rc);
goto cleanup;
}
rc = gVBoxAPI.UIVirtualBox.RegisterMachine(data->vboxObj, machine);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Unable to register Machine, rc=%08x"),
(unsigned)rc);
goto cleanup;
}
ret = 0;
cleanup:
VIR_FREE(def);
VIR_FREE(defXml);
VBOX_RELEASE(machine);
VBOX_UTF16_FREE(settingsFilePathUtf16);
VBOX_UTF8_FREE(settingsFilepath);
VIR_FREE(snapshotMachineDesc);
VBOX_UTF16_FREE(machineNameUtf16);
VBOX_UTF8_FREE(machineName);
VIR_FREE(machineLocationPath);
VIR_FREE(nameTmpUse);
return ret;
}
static int vboxDomainSnapshotDelete(virDomainSnapshotPtr snapshot,
unsigned int flags)
{
virDomainPtr dom = snapshot->domain;
VBOX_OBJECT_CHECK(dom->conn, int, -1);
vboxIIDUnion domiid;
IMachine *machine = NULL;
ISnapshot *snap = NULL;
IConsole *console = NULL;
PRUint32 state;
nsresult rc;
vboxArray snapChildren = VBOX_ARRAY_INITIALIZER;
virCheckFlags(VIR_DOMAIN_SNAPSHOT_DELETE_CHILDREN |
VIR_DOMAIN_SNAPSHOT_DELETE_METADATA_ONLY, -1);
if (openSessionForMachine(data, dom->uuid, &domiid, &machine, false) < 0)
goto cleanup;
snap = vboxDomainSnapshotGet(data, dom, machine, snapshot->name);
if (!snap)
goto cleanup;
rc = gVBoxAPI.UIMachine.GetState(machine, &state);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("could not get domain state"));
goto cleanup;
}
/* In case we just want to delete the metadata, we will edit the vbox file in order
*to remove the node concerning the snapshot
*/
if (flags & VIR_DOMAIN_SNAPSHOT_DELETE_METADATA_ONLY) {
rc = gVBoxAPI.UArray.vboxArrayGet(&snapChildren, snap,
gVBoxAPI.UArray.handleSnapshotGetChildren(snap));
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("could not get snapshot children"));
goto cleanup;
}
if (snapChildren.count != 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("cannot delete metadata of a snapshot with children"));
goto cleanup;
} else if (gVBoxAPI.vboxSnapshotRedefine) {
ret = vboxDomainSnapshotDeleteMetadataOnly(snapshot);
}
goto cleanup;
}
if (gVBoxAPI.machineStateChecker.Online(state)) {
virReportError(VIR_ERR_OPERATION_INVALID, "%s",
_("cannot delete snapshots of running domain"));
goto cleanup;
}
rc = gVBoxAPI.UISession.Open(data, &domiid, machine);
if (NS_SUCCEEDED(rc))
rc = gVBoxAPI.UISession.GetConsole(data->vboxSession, &console);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("could not open VirtualBox session with domain %s"),
dom->name);
goto cleanup;
}
if (flags & VIR_DOMAIN_SNAPSHOT_DELETE_CHILDREN)
ret = vboxDomainSnapshotDeleteTree(data, console, snap);
else
ret = vboxDomainSnapshotDeleteSingle(data, console, snap);
cleanup:
VBOX_RELEASE(console);
VBOX_RELEASE(snap);
vboxIIDUnalloc(&domiid);
gVBoxAPI.UISession.Close(data->vboxSession);
return ret;
}
static char *
vboxDomainScreenshot(virDomainPtr dom,
virStreamPtr st,
unsigned int screen,
unsigned int flags)
{
VBOX_OBJECT_CHECK(dom->conn, char *, NULL);
IConsole *console = NULL;
vboxIIDUnion iid;
IMachine *machine = NULL;
nsresult rc;
char *tmp;
int tmp_fd = -1;
unsigned int max_screen;
if (!gVBoxAPI.supportScreenshot) {
virReportError(VIR_ERR_NO_SUPPORT, "%s",
_("virDomainScreenshot don't support for current vbox version"));
return NULL;
}
virCheckFlags(0, NULL);
if (openSessionForMachine(data, dom->uuid, &iid, &machine, false) < 0)
return NULL;
rc = gVBoxAPI.UIMachine.GetMonitorCount(machine, &max_screen);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_OPERATION_FAILED, "%s",
_("unable to get monitor count"));
VBOX_RELEASE(machine);
return NULL;
}
if (screen >= max_screen) {
virReportError(VIR_ERR_INVALID_ARG,
_("screen ID higher than monitor "
"count (%d)"), max_screen);
VBOX_RELEASE(machine);
return NULL;
}
if (virAsprintf(&tmp, "%s/cache/libvirt/vbox.screendump.XXXXXX", LOCALSTATEDIR) < 0) {
VBOX_RELEASE(machine);
return NULL;
}
if ((tmp_fd = mkostemp(tmp, O_CLOEXEC)) == -1) {
virReportSystemError(errno, _("mkostemp(\"%s\") failed"), tmp);
VIR_FREE(tmp);
VBOX_RELEASE(machine);
return NULL;
}
rc = gVBoxAPI.UISession.OpenExisting(data, &iid, machine);
if (NS_SUCCEEDED(rc)) {
rc = gVBoxAPI.UISession.GetConsole(data->vboxSession, &console);
if (NS_SUCCEEDED(rc) && console) {
IDisplay *display = NULL;
gVBoxAPI.UIConsole.GetDisplay(console, &display);
if (display) {
PRUint32 width, height, bitsPerPixel;
PRUint32 screenDataSize;
PRUint8 *screenData;
PRInt32 xOrigin, yOrigin;
rc = gVBoxAPI.UIDisplay.GetScreenResolution(display, screen,
&width, &height,
&bitsPerPixel,
&xOrigin, &yOrigin);
if (NS_FAILED(rc) || !width || !height) {
virReportError(VIR_ERR_OPERATION_FAILED, "%s",
_("unable to get screen resolution"));
goto endjob;
}
rc = gVBoxAPI.UIDisplay.TakeScreenShotPNGToArray(display, screen,
width, height,
&screenDataSize,
&screenData);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_OPERATION_FAILED, "%s",
_("failed to take screenshot"));
goto endjob;
}
if (safewrite(tmp_fd, (char *) screenData,
screenDataSize) < 0) {
virReportSystemError(errno, _("unable to write data "
"to '%s'"), tmp);
goto endjob;
}
if (VIR_CLOSE(tmp_fd) < 0) {
virReportSystemError(errno, _("unable to close %s"), tmp);
goto endjob;
}
if (VIR_STRDUP(ret, "image/png") < 0)
goto endjob;
if (virFDStreamOpenFile(st, tmp, 0, 0, O_RDONLY) < 0) {
virReportError(VIR_ERR_OPERATION_FAILED, "%s",
_("unable to open stream"));
VIR_FREE(ret);
}
endjob:
VIR_FREE(screenData);
VBOX_RELEASE(display);
}
VBOX_RELEASE(console);
}
gVBoxAPI.UISession.Close(data->vboxSession);
}
VIR_FORCE_CLOSE(tmp_fd);
unlink(tmp);
VIR_FREE(tmp);
VBOX_RELEASE(machine);
vboxIIDUnalloc(&iid);
return ret;
}
#define MATCH(FLAG) (flags & (FLAG))
static int
vboxConnectListAllDomains(virConnectPtr conn,
virDomainPtr **domains,
unsigned int flags)
{
VBOX_OBJECT_CHECK(conn, int, -1);
vboxArray machines = VBOX_ARRAY_INITIALIZER;
char *machineNameUtf8 = NULL;
PRUnichar *machineNameUtf16 = NULL;
unsigned char uuid[VIR_UUID_BUFLEN];
vboxIIDUnion iid;
PRUint32 state;
nsresult rc;
size_t i;
virDomainPtr dom;
virDomainPtr *doms = NULL;
int count = 0;
bool active;
PRUint32 snapshotCount;
virCheckFlags(VIR_CONNECT_LIST_DOMAINS_FILTERS_ALL, -1);
/* filter out flag options that will produce 0 results in vbox driver:
* - managed save: vbox guests don't have managed save images
* - autostart: vbox doesn't support autostarting guests
* - persistance: vbox doesn't support transient guests
*/
if ((MATCH(VIR_CONNECT_LIST_DOMAINS_TRANSIENT) &&
!MATCH(VIR_CONNECT_LIST_DOMAINS_PERSISTENT)) ||
(MATCH(VIR_CONNECT_LIST_DOMAINS_AUTOSTART) &&
!MATCH(VIR_CONNECT_LIST_DOMAINS_NO_AUTOSTART)) ||
(MATCH(VIR_CONNECT_LIST_DOMAINS_MANAGEDSAVE) &&
!MATCH(VIR_CONNECT_LIST_DOMAINS_NO_MANAGEDSAVE))) {
if (domains &&
VIR_ALLOC_N(*domains, 1) < 0)
goto cleanup;
ret = 0;
goto cleanup;
}
rc = gVBoxAPI.UArray.vboxArrayGet(&machines, data->vboxObj, ARRAY_GET_MACHINES);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Could not get list of domains, rc=%08x"), (unsigned)rc);
goto cleanup;
}
if (domains &&
VIR_ALLOC_N(doms, machines.count + 1) < 0)
goto cleanup;
for (i = 0; i < machines.count; i++) {
IMachine *machine = machines.items[i];
if (!machine)
continue;
PRBool isAccessible = PR_FALSE;
gVBoxAPI.UIMachine.GetAccessible(machine, &isAccessible);
if (!isAccessible)
continue;
gVBoxAPI.UIMachine.GetState(machine, &state);
if (gVBoxAPI.machineStateChecker.Online(state))
active = true;
else
active = false;
/* filter by active state */
if (MATCH(VIR_CONNECT_LIST_DOMAINS_FILTERS_ACTIVE) &&
!((MATCH(VIR_CONNECT_LIST_DOMAINS_ACTIVE) && active) ||
(MATCH(VIR_CONNECT_LIST_DOMAINS_INACTIVE) && !active)))
continue;
/* filter by snapshot existence */
if (MATCH(VIR_CONNECT_LIST_DOMAINS_FILTERS_SNAPSHOT)) {
rc = gVBoxAPI.UIMachine.GetSnapshotCount(machine, &snapshotCount);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("could not get snapshot count for listed domains"));
goto cleanup;
}
if (!((MATCH(VIR_CONNECT_LIST_DOMAINS_HAS_SNAPSHOT) &&
snapshotCount > 0) ||
(MATCH(VIR_CONNECT_LIST_DOMAINS_NO_SNAPSHOT) &&
snapshotCount == 0)))
continue;
}
/* filter by machine state */
if (MATCH(VIR_CONNECT_LIST_DOMAINS_FILTERS_STATE) &&
!((MATCH(VIR_CONNECT_LIST_DOMAINS_RUNNING) &&
gVBoxAPI.machineStateChecker.Running(state)) ||
(MATCH(VIR_CONNECT_LIST_DOMAINS_PAUSED) &&
gVBoxAPI.machineStateChecker.Paused(state)) ||
(MATCH(VIR_CONNECT_LIST_DOMAINS_SHUTOFF) &&
gVBoxAPI.machineStateChecker.PoweredOff(state)) ||
(MATCH(VIR_CONNECT_LIST_DOMAINS_OTHER) &&
(!gVBoxAPI.machineStateChecker.Running(state) &&
!gVBoxAPI.machineStateChecker.Paused(state) &&
!gVBoxAPI.machineStateChecker.PoweredOff(state)))))
continue;
/* just count the machines */
if (!doms) {
count++;
continue;
}
gVBoxAPI.UIMachine.GetName(machine, &machineNameUtf16);
VBOX_UTF16_TO_UTF8(machineNameUtf16, &machineNameUtf8);
gVBoxAPI.UIMachine.GetId(machine, &iid);
vboxIIDToUUID(&iid, uuid);
vboxIIDUnalloc(&iid);
dom = virGetDomain(conn, machineNameUtf8, uuid);
VBOX_UTF8_FREE(machineNameUtf8);
VBOX_UTF16_FREE(machineNameUtf16);
if (!dom)
goto cleanup;
if (active)
dom->id = i + 1;
doms[count++] = dom;
}
if (doms) {
/* safe to ignore, new size will be equal or less than
* previous allocation*/
ignore_value(VIR_REALLOC_N(doms, count + 1));
*domains = doms;
doms = NULL;
}
ret = count;
cleanup:
if (doms) {
for (i = 0; i < count; i++) {
if (doms[i])
virDomainFree(doms[i]);
}
}
VIR_FREE(doms);
gVBoxAPI.UArray.vboxArrayRelease(&machines);
return ret;
}
#undef MATCH
static int
vboxNodeGetInfo(virConnectPtr conn ATTRIBUTE_UNUSED,
virNodeInfoPtr nodeinfo)
{
return nodeGetInfo(nodeinfo);
}
static int
vboxNodeGetCellsFreeMemory(virConnectPtr conn ATTRIBUTE_UNUSED,
unsigned long long *freeMems,
int startCell,
int maxCells)
{
return nodeGetCellsFreeMemory(freeMems, startCell, maxCells);
}
static unsigned long long
vboxNodeGetFreeMemory(virConnectPtr conn ATTRIBUTE_UNUSED)
{
unsigned long long freeMem;
if (nodeGetMemory(NULL, &freeMem) < 0)
return 0;
return freeMem;
}
static int
vboxNodeGetFreePages(virConnectPtr conn ATTRIBUTE_UNUSED,
unsigned int npages,
unsigned int *pages,
int startCell,
unsigned int cellCount,
unsigned long long *counts,
unsigned int flags)
{
virCheckFlags(0, -1);
return nodeGetFreePages(npages, pages, startCell, cellCount, counts);
}
static int
vboxNodeAllocPages(virConnectPtr conn ATTRIBUTE_UNUSED,
unsigned int npages,
unsigned int *pageSizes,
unsigned long long *pageCounts,
int startCell,
unsigned int cellCount,
unsigned int flags)
{
bool add = !(flags & VIR_NODE_ALLOC_PAGES_SET);
virCheckFlags(VIR_NODE_ALLOC_PAGES_SET, -1);
return nodeAllocPages(npages, pageSizes, pageCounts,
startCell, cellCount, add);
}
/**
* Function Tables
*/
virDriver vboxCommonDriver = {
.no = VIR_DRV_VBOX,
.name = "VBOX",
.connectOpen = vboxConnectOpen, /* 0.6.3 */
.connectClose = vboxConnectClose, /* 0.6.3 */
.connectGetVersion = vboxConnectGetVersion, /* 0.6.3 */
.connectGetHostname = vboxConnectGetHostname, /* 0.6.3 */
.connectGetMaxVcpus = vboxConnectGetMaxVcpus, /* 0.6.3 */
.nodeGetInfo = vboxNodeGetInfo, /* 0.6.3 */
.connectGetCapabilities = vboxConnectGetCapabilities, /* 0.6.3 */
.connectListDomains = vboxConnectListDomains, /* 0.6.3 */
.connectNumOfDomains = vboxConnectNumOfDomains, /* 0.6.3 */
.connectListAllDomains = vboxConnectListAllDomains, /* 0.9.13 */
.domainCreateXML = vboxDomainCreateXML, /* 0.6.3 */
.domainLookupByID = vboxDomainLookupByID, /* 0.6.3 */
.domainLookupByUUID = vboxDomainLookupByUUID, /* 0.6.3 */
.domainLookupByName = vboxDomainLookupByName, /* 0.6.3 */
.domainSuspend = vboxDomainSuspend, /* 0.6.3 */
.domainResume = vboxDomainResume, /* 0.6.3 */
.domainShutdown = vboxDomainShutdown, /* 0.6.3 */
.domainShutdownFlags = vboxDomainShutdownFlags, /* 0.9.10 */
.domainReboot = vboxDomainReboot, /* 0.6.3 */
.domainDestroy = vboxDomainDestroy, /* 0.6.3 */
.domainDestroyFlags = vboxDomainDestroyFlags, /* 0.9.4 */
.domainGetOSType = vboxDomainGetOSType, /* 0.6.3 */
.domainSetMemory = vboxDomainSetMemory, /* 0.6.3 */
.domainGetInfo = vboxDomainGetInfo, /* 0.6.3 */
.domainGetState = vboxDomainGetState, /* 0.9.2 */
.domainSave = vboxDomainSave, /* 0.6.3 */
.domainSetVcpus = vboxDomainSetVcpus, /* 0.7.1 */
.domainSetVcpusFlags = vboxDomainSetVcpusFlags, /* 0.8.5 */
.domainGetVcpusFlags = vboxDomainGetVcpusFlags, /* 0.8.5 */
.domainGetMaxVcpus = vboxDomainGetMaxVcpus, /* 0.7.1 */
.domainGetXMLDesc = vboxDomainGetXMLDesc, /* 0.6.3 */
.connectListDefinedDomains = vboxConnectListDefinedDomains, /* 0.6.3 */
.connectNumOfDefinedDomains = vboxConnectNumOfDefinedDomains, /* 0.6.3 */
.domainCreate = vboxDomainCreate, /* 0.6.3 */
.domainCreateWithFlags = vboxDomainCreateWithFlags, /* 0.8.2 */
.domainDefineXML = vboxDomainDefineXML, /* 0.6.3 */
.domainUndefine = vboxDomainUndefine, /* 0.6.3 */
.domainUndefineFlags = vboxDomainUndefineFlags, /* 0.9.5 */
.domainAttachDevice = vboxDomainAttachDevice, /* 0.6.3 */
.domainAttachDeviceFlags = vboxDomainAttachDeviceFlags, /* 0.7.7 */
.domainDetachDevice = vboxDomainDetachDevice, /* 0.6.3 */
.domainDetachDeviceFlags = vboxDomainDetachDeviceFlags, /* 0.7.7 */
.domainUpdateDeviceFlags = vboxDomainUpdateDeviceFlags, /* 0.8.0 */
.nodeGetCellsFreeMemory = vboxNodeGetCellsFreeMemory, /* 0.6.5 */
.nodeGetFreeMemory = vboxNodeGetFreeMemory, /* 0.6.5 */
.connectIsEncrypted = vboxConnectIsEncrypted, /* 0.7.3 */
.connectIsSecure = vboxConnectIsSecure, /* 0.7.3 */
.domainIsActive = vboxDomainIsActive, /* 0.7.3 */
.domainIsPersistent = vboxDomainIsPersistent, /* 0.7.3 */
.domainIsUpdated = vboxDomainIsUpdated, /* 0.8.6 */
.domainSnapshotCreateXML = vboxDomainSnapshotCreateXML, /* 0.8.0 */
.domainSnapshotGetXMLDesc = vboxDomainSnapshotGetXMLDesc, /* 0.8.0 */
.domainSnapshotNum = vboxDomainSnapshotNum, /* 0.8.0 */
.domainSnapshotListNames = vboxDomainSnapshotListNames, /* 0.8.0 */
.domainSnapshotLookupByName = vboxDomainSnapshotLookupByName, /* 0.8.0 */
.domainHasCurrentSnapshot = vboxDomainHasCurrentSnapshot, /* 0.8.0 */
.domainSnapshotGetParent = vboxDomainSnapshotGetParent, /* 0.9.7 */
.domainSnapshotCurrent = vboxDomainSnapshotCurrent, /* 0.8.0 */
.domainSnapshotIsCurrent = vboxDomainSnapshotIsCurrent, /* 0.9.13 */
.domainSnapshotHasMetadata = vboxDomainSnapshotHasMetadata, /* 0.9.13 */
.domainRevertToSnapshot = vboxDomainRevertToSnapshot, /* 0.8.0 */
.domainSnapshotDelete = vboxDomainSnapshotDelete, /* 0.8.0 */
.connectIsAlive = vboxConnectIsAlive, /* 0.9.8 */
.nodeGetFreePages = vboxNodeGetFreePages, /* 1.2.6 */
.nodeAllocPages = vboxNodeAllocPages, /* 1.2.9 */
};
static void updateDriver(void)
{
/* Update the vboxDriver according to the vboxUniformedAPI.
* We need to make sure the vboxUniformedAPI is initialized
* before calling this function. */
gVBoxAPI.registerDomainEvent(&vboxCommonDriver);
if (gVBoxAPI.supportScreenshot)
vboxCommonDriver.domainScreenshot = vboxDomainScreenshot;
else
vboxCommonDriver.domainScreenshot = NULL;
}
virDriverPtr vboxGetDriver(uint32_t uVersion)
{
/* Install gVBoxAPI according to the vbox API version.
* Return -1 for unsupported version.
*/
if (uVersion >= 2001052 && uVersion < 2002051) {
vbox22InstallUniformedAPI(&gVBoxAPI);
} else if (uVersion >= 2002051 && uVersion < 3000051) {
vbox30InstallUniformedAPI(&gVBoxAPI);
} else if (uVersion >= 3000051 && uVersion < 3001051) {
vbox31InstallUniformedAPI(&gVBoxAPI);
} else if (uVersion >= 3001051 && uVersion < 3002051) {
vbox32InstallUniformedAPI(&gVBoxAPI);
} else if (uVersion >= 3002051 && uVersion < 4000051) {
vbox40InstallUniformedAPI(&gVBoxAPI);
} else if (uVersion >= 4000051 && uVersion < 4001051) {
vbox41InstallUniformedAPI(&gVBoxAPI);
} else if (uVersion >= 4001051 && uVersion < 4002020) {
vbox42InstallUniformedAPI(&gVBoxAPI);
} else if (uVersion >= 4002020 && uVersion < 4002051) {
vbox42_20InstallUniformedAPI(&gVBoxAPI);
} else if (uVersion >= 4002051 && uVersion < 4003004) {
vbox43InstallUniformedAPI(&gVBoxAPI);
} else if (uVersion >= 4003004 && uVersion < 4003051) {
vbox43_4InstallUniformedAPI(&gVBoxAPI);
} else {
return NULL;
}
updateDriver();
return &vboxCommonDriver;
}