/*
* Copyright (C) 2014, Taowei Luo (uaedante@gmail.com)
* Copyright (C) 2010-2016 Red Hat, Inc.
* Copyright (C) 2008-2009 Sun Microsystems, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* .
*/
#include
#include
#include
#include "internal.h"
#include "datatypes.h"
#include "virdomainobjlist.h"
#include "domain_event.h"
#include "virlog.h"
#include "viralloc.h"
#include "virhostmem.h"
#include "virstring.h"
#include "virfile.h"
#include "virtime.h"
#include "virkeycode.h"
#include "snapshot_conf.h"
#include "vbox_snapshot_conf.h"
#include "virfdstream.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");
/* global vbox API, used for all common codes. */
static vboxUniformedAPI gVBoxAPI;
static virClassPtr vboxDriverClass;
static virMutex vbox_driver_lock = VIR_MUTEX_INITIALIZER;
static vboxDriverPtr vbox_driver;
static vboxDriverPtr vboxDriverObjNew(void);
static virDomainDefParserConfig vboxDomainDefParserConfig = {
.macPrefix = { 0x08, 0x00, 0x27 },
.features = VIR_DOMAIN_DEF_FEATURE_NAME_SLASH,
};
static virCapsPtr
vboxCapsInit(void)
{
virCapsPtr caps;
virCapsGuestPtr guest;
if ((caps = virCapabilitiesNew(virArchFromHost(),
false, false)) == NULL)
goto no_memory;
if (virCapabilitiesInitNUMA(caps) < 0)
goto no_memory;
if (virCapabilitiesInitCaches(caps) < 0)
goto no_memory;
if ((guest = virCapabilitiesAddGuest(caps,
VIR_DOMAIN_OSTYPE_HVM,
caps->host.arch,
NULL,
NULL,
0,
NULL)) == NULL)
goto no_memory;
if (virCapabilitiesAddGuestDomain(guest,
VIR_DOMAIN_VIRT_VBOX,
NULL,
NULL,
0,
NULL) == NULL)
goto no_memory;
return caps;
no_memory:
virObjectUnref(caps);
return NULL;
}
static void
vboxDriverDispose(void *obj)
{
vboxDriverPtr driver = obj;
virObjectUnref(driver->caps);
virObjectUnref(driver->xmlopt);
}
static int
vboxDriverOnceInit(void)
{
if (!(vboxDriverClass = virClassNew(virClassForObjectLockable(),
"vboxDriver",
sizeof(vboxDriver),
vboxDriverDispose)))
return -1;
return 0;
}
VIR_ONCE_GLOBAL_INIT(vboxDriver);
static vboxDriverPtr
vboxDriverObjNew(void)
{
vboxDriverPtr driver;
if (vboxDriverInitialize() < 0)
return NULL;
if (!(driver = virObjectLockableNew(vboxDriverClass)))
return NULL;
if (!(driver->caps = vboxCapsInit()) ||
!(driver->xmlopt = virDomainXMLOptionNew(&vboxDomainDefParserConfig,
NULL, NULL, NULL, NULL)))
goto cleanup;
return driver;
cleanup:
virObjectUnref(driver);
return NULL;
}
static int
vboxExtractVersion(void)
{
int ret = -1;
PRUnichar *versionUtf16 = NULL;
char *vboxVersion = NULL;
nsresult rc;
if (vbox_driver->version > 0)
return 0;
rc = gVBoxAPI.UIVirtualBox.GetVersion(vbox_driver->vboxObj, &versionUtf16);
if (NS_FAILED(rc))
goto failed;
gVBoxAPI.UPFN.Utf16ToUtf8(vbox_driver->pFuncs, versionUtf16, &vboxVersion);
if (virParseVersionString(vboxVersion, &vbox_driver->version, false) >= 0)
ret = 0;
gVBoxAPI.UPFN.Utf8Free(vbox_driver->pFuncs, vboxVersion);
gVBoxAPI.UPFN.ComUnallocMem(vbox_driver->pFuncs, versionUtf16);
vboxVersion = NULL;
versionUtf16 = NULL;
failed:
if (ret != 0)
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Could not extract VirtualBox version"));
return ret;
}
static int
vboxSdkInitialize(void)
{
/* vbox API was already initialized by first connection */
if (vbox_driver->connectionCount > 0)
return 0;
if (gVBoxAPI.UPFN.Initialize(vbox_driver) != 0)
return -1;
if (vbox_driver->vboxObj == NULL) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("IVirtualBox object is null"));
return -1;
}
if (vbox_driver->vboxSession == NULL) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("ISession object is null"));
return -1;
}
return 0;
}
static void
vboxSdkUninitialize(void)
{
/* do not uninitialize, when there are still connection using it */
if (vbox_driver->connectionCount > 0)
return;
gVBoxAPI.UPFN.Uninitialize(vbox_driver);
}
static vboxDriverPtr
vboxGetDriverConnection(void)
{
virMutexLock(&vbox_driver_lock);
if (vbox_driver) {
virObjectRef(vbox_driver);
} else {
vbox_driver = vboxDriverObjNew();
if (!vbox_driver) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Failed to create vbox driver object."));
return NULL;
}
}
if (vboxSdkInitialize() < 0 || vboxExtractVersion() < 0) {
gVBoxAPI.UPFN.Uninitialize(vbox_driver);
/* make sure to clear the pointer when last reference was released */
if (!virObjectUnref(vbox_driver))
vbox_driver = NULL;
virMutexUnlock(&vbox_driver_lock);
return NULL;
}
vbox_driver->connectionCount++;
virMutexUnlock(&vbox_driver_lock);
return vbox_driver;
}
static void
vboxDestroyDriverConnection(void)
{
virMutexLock(&vbox_driver_lock);
if (!vbox_driver)
goto cleanup;
vbox_driver->connectionCount--;
vboxSdkUninitialize();
if (!virObjectUnref(vbox_driver))
vbox_driver = NULL;
cleanup:
virMutexUnlock(&vbox_driver_lock);
}
static int openSessionForMachine(vboxDriverPtr data, const unsigned char *dom_uuid,
vboxIID *iid, IMachine **machine)
{
VBOX_IID_INITIALIZE(iid);
vboxIIDFromUUID(iid, dom_uuid);
/* 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 generate the name for medium,
* for e.g: hda, sda, etc
*
* @returns null terminated string with device name or NULL
* for failures
* @param storageBus Input storage bus type
* @param devicePort Input port number
* @param deviceSlot Input slot number
* @param sdCount Running total of disk devices with "sd" prefix
*/
static char *
vboxGenerateMediumName(PRUint32 storageBus,
PRInt32 devicePort,
PRInt32 deviceSlot,
size_t sdCount)
{
const char *prefix = NULL;
char *name = NULL;
int total = 0;
switch ((enum StorageBus) storageBus) {
case StorageBus_IDE:
prefix = "hd";
total = devicePort * 2 + deviceSlot;
break;
case StorageBus_SATA:
case StorageBus_SCSI:
case StorageBus_SAS:
prefix = "sd";
total = sdCount;
break;
case StorageBus_Floppy:
total = deviceSlot;
prefix = "fd";
break;
case StorageBus_Null:
return NULL;
}
name = virIndexToDiskName(total, prefix);
return name;
}
static int
vboxSetStorageController(virDomainControllerDefPtr controller,
vboxDriverPtr data,
IMachine *machine)
{
PRUnichar *controllerName = NULL;
PRInt32 vboxModel = StorageControllerType_Null;
PRInt32 vboxBusType = StorageBus_Null;
IStorageController *vboxController = NULL;
nsresult rc = 0;
char *debugName = NULL;
int ret = -1;
/* libvirt controller type => vbox bus type */
switch ((virDomainControllerType) controller->type) {
case VIR_DOMAIN_CONTROLLER_TYPE_FDC:
VBOX_UTF8_TO_UTF16(VBOX_CONTROLLER_FLOPPY_NAME, &controllerName);
vboxBusType = StorageBus_Floppy;
break;
case VIR_DOMAIN_CONTROLLER_TYPE_IDE:
VBOX_UTF8_TO_UTF16(VBOX_CONTROLLER_IDE_NAME, &controllerName);
vboxBusType = StorageBus_IDE;
break;
case VIR_DOMAIN_CONTROLLER_TYPE_SCSI:
VBOX_UTF8_TO_UTF16(VBOX_CONTROLLER_SCSI_NAME, &controllerName);
vboxBusType = StorageBus_SCSI;
break;
case VIR_DOMAIN_CONTROLLER_TYPE_SATA:
VBOX_UTF8_TO_UTF16(VBOX_CONTROLLER_SATA_NAME, &controllerName);
vboxBusType = StorageBus_SATA;
break;
case VIR_DOMAIN_CONTROLLER_TYPE_VIRTIO_SERIAL:
case VIR_DOMAIN_CONTROLLER_TYPE_CCID:
case VIR_DOMAIN_CONTROLLER_TYPE_USB:
case VIR_DOMAIN_CONTROLLER_TYPE_PCI:
case VIR_DOMAIN_CONTROLLER_TYPE_LAST:
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("The vbox driver does not support %s controller type"),
virDomainControllerTypeToString(controller->type));
return -1;
}
/* libvirt scsi model => vbox scsi model */
if (controller->type == VIR_DOMAIN_CONTROLLER_TYPE_SCSI) {
switch ((virDomainControllerModelSCSI) controller->model) {
case VIR_DOMAIN_CONTROLLER_MODEL_SCSI_LSILOGIC:
case VIR_DOMAIN_CONTROLLER_MODEL_SCSI_AUTO:
vboxModel = StorageControllerType_LsiLogic;
break;
case VIR_DOMAIN_CONTROLLER_MODEL_SCSI_BUSLOGIC:
vboxModel = StorageControllerType_BusLogic;
break;
case VIR_DOMAIN_CONTROLLER_MODEL_SCSI_LSISAS1068:
/* in vbox, lsisas has a dedicated SAS bus type with no model */
VBOX_UTF16_FREE(controllerName);
VBOX_UTF8_TO_UTF16(VBOX_CONTROLLER_SAS_NAME, &controllerName);
vboxBusType = StorageBus_SAS;
break;
case VIR_DOMAIN_CONTROLLER_MODEL_SCSI_VMPVSCSI:
case VIR_DOMAIN_CONTROLLER_MODEL_SCSI_IBMVSCSI:
case VIR_DOMAIN_CONTROLLER_MODEL_SCSI_VIRTIO_SCSI:
case VIR_DOMAIN_CONTROLLER_MODEL_SCSI_LSISAS1078:
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("The vbox driver does not support %s SCSI "
"controller model"),
virDomainControllerModelSCSITypeToString(controller->model));
goto cleanup;
case VIR_DOMAIN_CONTROLLER_MODEL_SCSI_DEFAULT:
case VIR_DOMAIN_CONTROLLER_MODEL_SCSI_LAST:
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Unexpected SCSI controller model %d"),
controller->model);
goto cleanup;
}
/* libvirt ide model => vbox ide model */
} else if (controller->type == VIR_DOMAIN_CONTROLLER_TYPE_IDE) {
switch ((virDomainControllerModelIDE) controller->model) {
case VIR_DOMAIN_CONTROLLER_MODEL_IDE_PIIX3:
vboxModel = StorageControllerType_PIIX3;
break;
case VIR_DOMAIN_CONTROLLER_MODEL_IDE_PIIX4:
vboxModel = StorageControllerType_PIIX4;
break;
case VIR_DOMAIN_CONTROLLER_MODEL_IDE_ICH6:
vboxModel = StorageControllerType_ICH6;
break;
case VIR_DOMAIN_CONTROLLER_MODEL_IDE_LAST:
case VIR_DOMAIN_CONTROLLER_MODEL_IDE_DEFAULT:
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("Unexpected IDE controller model %d"),
controller->model);
goto cleanup;
}
}
VBOX_UTF16_TO_UTF8(controllerName, &debugName);
VIR_DEBUG("Adding VBOX storage controller (name: %s, busType: %d)",
debugName, vboxBusType);
rc = gVBoxAPI.UIMachine.AddStorageController(machine, controllerName,
vboxBusType, &vboxController);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Failed to add storage controller "
"(name: %s, busType: %d), rc=%08x"),
debugName, vboxBusType, rc);
goto cleanup;
}
/* only IDE or SCSI controller have model choices */
if (vboxModel != StorageControllerType_Null) {
rc = gVBoxAPI.UIStorageController.SetControllerType(vboxController,
vboxModel);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Failed to change storage controller model, "
"rc=%08x"), rc);
goto cleanup;
}
}
ret = 0;
cleanup:
VBOX_UTF16_FREE(controllerName);
VBOX_UTF8_FREE(debugName);
VBOX_RELEASE(vboxController);
return ret;
}
static int
vboxAttachStorageControllers(virDomainDefPtr def,
vboxDriverPtr data,
IMachine *machine)
{
size_t i;
for (i = 0; i < def->ncontrollers; i++) {
if (vboxSetStorageController(def->controllers[i], data, machine) < 0)
return -1;
}
return 0;
}
static int
vboxConnectURIProbe(char **uri)
{
return VIR_STRDUP(*uri, geteuid() ? "vbox:///session" : "vbox:///system");
}
static virDrvOpenStatus
vboxConnectOpen(virConnectPtr conn,
virConnectAuthPtr auth ATTRIBUTE_UNUSED,
virConfPtr conf ATTRIBUTE_UNUSED,
unsigned int flags)
{
vboxDriverPtr driver = NULL;
uid_t uid = geteuid();
virCheckFlags(VIR_CONNECT_RO, VIR_DRV_OPEN_ERROR);
if (conn->uri == NULL)
return VIR_DRV_OPEN_DECLINED;
if (conn->uri->scheme == NULL ||
STRNEQ(conn->uri->scheme, "vbox"))
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 (!(driver = vboxGetDriverConnection()))
return VIR_DRV_OPEN_ERROR;
conn->privateData = virObjectRef(driver);
VIR_DEBUG("in vboxOpen");
return VIR_DRV_OPEN_SUCCESS;
}
static int vboxConnectClose(virConnectPtr conn)
{
VIR_DEBUG("%s: in vboxClose", conn->driver->name);
virObjectUnref(conn->privateData);
vboxDestroyDriverConnection();
return 0;
}
static int
vboxDomainSave(virDomainPtr dom, const char *path ATTRIBUTE_UNUSED)
{
vboxDriverPtr data = dom->conn->privateData;
IConsole *console = NULL;
vboxIID iid;
IMachine *machine = NULL;
IProgress *progress = NULL;
resultCodeUnion resultCode;
nsresult rc;
int ret = -1;
if (!data->vboxObj)
return ret;
/* 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) < 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 int vboxConnectGetVersion(virConnectPtr conn, unsigned long *version)
{
vboxDriverPtr data = conn->privateData;
VIR_DEBUG("%s: in vboxGetVersion", conn->driver->name);
virObjectLock(data);
*version = data->version;
virObjectUnlock(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)
{
vboxDriverPtr data = conn->privateData;
PRUint32 maxCPUCount = 0;
int ret = -1;
if (!data->vboxObj)
return ret;
/* 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)
{
vboxDriverPtr data = conn->privateData;
char *ret = NULL;
if (!data->vboxObj)
return ret;
virObjectLock(data);
ret = virCapabilitiesFormatXML(data->caps);
virObjectUnlock(data);
return ret;
}
static int vboxConnectListDomains(virConnectPtr conn, int *ids, int nids)
{
vboxDriverPtr data = conn->privateData;
vboxArray machines = VBOX_ARRAY_INITIALIZER;
PRUint32 state;
nsresult rc;
size_t i, j;
int ret = -1;
if (!data->vboxObj)
return ret;
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)
{
vboxDriverPtr data = conn->privateData;
vboxArray machines = VBOX_ARRAY_INITIALIZER;
PRUint32 state;
nsresult rc;
size_t i;
int ret = -1;
if (!data->vboxObj)
return ret;
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)
{
vboxDriverPtr data = conn->privateData;
vboxArray machines = VBOX_ARRAY_INITIALIZER;
IMachine *machine;
PRBool isAccessible = PR_FALSE;
PRUnichar *machineNameUtf16 = NULL;
char *machineNameUtf8 = NULL;
vboxIID iid;
unsigned char uuid[VIR_UUID_BUFLEN];
PRUint32 state;
nsresult rc;
virDomainPtr ret = NULL;
if (!data->vboxObj)
return ret;
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, 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)
{
vboxDriverPtr data = conn->privateData;
vboxArray machines = VBOX_ARRAY_INITIALIZER;
vboxIID iid;
char *machineNameUtf8 = NULL;
PRUnichar *machineNameUtf16 = NULL;
unsigned char iid_as_uuid[VIR_UUID_BUFLEN];
size_t i;
bool matched = false;
nsresult rc;
virDomainPtr ret = NULL;
if (!data->vboxObj)
return ret;
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;
int id = -1;
matched = true;
gVBoxAPI.UIMachine.GetName(machine, &machineNameUtf16);
VBOX_UTF16_TO_UTF8(machineNameUtf16, &machineNameUtf8);
gVBoxAPI.UIMachine.GetState(machine, &state);
if (gVBoxAPI.machineStateChecker.Online(state))
id = i + 1;
ret = virGetDomain(conn, machineNameUtf8, iid_as_uuid, id);
}
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)
{
vboxDriverPtr data = conn->privateData;
vboxArray machines = VBOX_ARRAY_INITIALIZER;
vboxIID iid;
char *machineNameUtf8 = NULL;
PRUnichar *machineNameUtf16 = NULL;
unsigned char uuid[VIR_UUID_BUFLEN];
size_t i;
bool matched = false;
nsresult rc;
virDomainPtr ret = NULL;
if (!data->vboxObj)
return ret;
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;
int id = -1;
matched = true;
gVBoxAPI.UIMachine.GetId(machine, &iid);
vboxIIDToUUID(&iid, uuid);
vboxIIDUnalloc(&iid);
gVBoxAPI.UIMachine.GetState(machine, &state);
if (gVBoxAPI.machineStateChecker.Online(state))
id = i + 1;
ret = virGetDomain(conn, machineNameUtf8, uuid, id);
}
VBOX_UTF8_FREE(machineNameUtf8);
VBOX_COM_UNALLOC_MEM(machineNameUtf16);
if (matched)
break;
}
gVBoxAPI.UArray.vboxArrayRelease(&machines);
return ret;
}
static void
vboxSetBootDeviceOrder(virDomainDefPtr def, vboxDriverPtr data,
IMachine *machine)
{
ISystemProperties *systemProperties = NULL;
PRUint32 maxBootPosition = 0;
size_t i = 0;
VIR_DEBUG("def->os.type %s", virDomainOSTypeToString(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 int
vboxAttachDrives(virDomainDefPtr def, vboxDriverPtr data, IMachine *machine)
{
size_t i;
int type, ret = 0;
const char *src = NULL;
nsresult rc = 0;
virDomainDiskDefPtr disk = NULL;
virDomainControllerDefPtr cont;
PRUnichar *storageCtlName = NULL;
char *controllerName = NULL;
IMedium *medium = NULL;
PRUnichar *mediumFileUtf16 = NULL;
PRUint32 devicePort, deviceSlot, deviceType, accessMode;
vboxIID mediumUUID;
VBOX_IID_INITIALIZE(&mediumUUID);
for (i = 0; i < def->ndisks; i++) {
disk = def->disks[i];
src = virDomainDiskGetSource(disk);
type = virDomainDiskGetType(disk);
deviceType = DeviceType_Null;
accessMode = AccessMode_ReadOnly;
devicePort = disk->info.addr.drive.unit;
deviceSlot = disk->info.addr.drive.bus;
if (type != VIR_STORAGE_TYPE_FILE) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("Unsupported storage type %s, the only supported "
"type is %s"),
virStorageTypeToString(type),
virStorageTypeToString(VIR_STORAGE_TYPE_FILE));
ret = -1;
goto cleanup;
}
switch ((virDomainDiskDevice) disk->device) {
case VIR_DOMAIN_DISK_DEVICE_DISK:
if (!src) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("Missing disk source file path"));
ret = -1;
goto cleanup;
}
deviceType = DeviceType_HardDisk;
accessMode = AccessMode_ReadWrite;
break;
case VIR_DOMAIN_DISK_DEVICE_CDROM:
deviceType = DeviceType_DVD;
accessMode = AccessMode_ReadOnly;
break;
case VIR_DOMAIN_DISK_DEVICE_FLOPPY:
deviceType = DeviceType_Floppy;
accessMode = AccessMode_ReadWrite;
break;
case VIR_DOMAIN_DISK_DEVICE_LUN:
case VIR_DOMAIN_DISK_DEVICE_LAST:
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("The vbox driver does not support %s disk device"),
virDomainDiskDeviceTypeToString(disk->device));
ret = -1;
goto cleanup;
}
switch ((virDomainDiskBus) disk->bus) {
case VIR_DOMAIN_DISK_BUS_IDE:
VBOX_UTF8_TO_UTF16(VBOX_CONTROLLER_IDE_NAME, &storageCtlName);
devicePort = def->disks[i]->info.addr.drive.bus;
deviceSlot = def->disks[i]->info.addr.drive.unit;
break;
case VIR_DOMAIN_DISK_BUS_SATA:
VBOX_UTF8_TO_UTF16(VBOX_CONTROLLER_SATA_NAME, &storageCtlName);
break;
case VIR_DOMAIN_DISK_BUS_SCSI:
VBOX_UTF8_TO_UTF16(VBOX_CONTROLLER_SCSI_NAME, &storageCtlName);
cont = virDomainDeviceFindSCSIController(def, &disk->info);
if (cont && cont->model == VIR_DOMAIN_CONTROLLER_MODEL_SCSI_LSISAS1068) {
VBOX_UTF16_FREE(storageCtlName);
VBOX_UTF8_TO_UTF16(VBOX_CONTROLLER_SAS_NAME, &storageCtlName);
}
break;
case VIR_DOMAIN_DISK_BUS_FDC:
VBOX_UTF8_TO_UTF16(VBOX_CONTROLLER_FLOPPY_NAME, &storageCtlName);
devicePort = 0;
deviceSlot = disk->info.addr.drive.unit;
break;
case VIR_DOMAIN_DISK_BUS_VIRTIO:
case VIR_DOMAIN_DISK_BUS_XEN:
case VIR_DOMAIN_DISK_BUS_USB:
case VIR_DOMAIN_DISK_BUS_UML:
case VIR_DOMAIN_DISK_BUS_SD:
case VIR_DOMAIN_DISK_BUS_LAST:
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("The vbox driver does not support %s bus type"),
virDomainDiskBusTypeToString(disk->bus));
ret = -1;
goto cleanup;
}
/* If disk source is specified, lookup IMedium - removable drives don't
* have either.
*/
if (src) {
VBOX_UTF8_TO_UTF16(src, &mediumFileUtf16);
VIR_DEBUG("Looking up medium %s, type: %d, mode: %d", src,
deviceType, accessMode);
rc = gVBoxAPI.UIVirtualBox.FindHardDisk(data->vboxObj, mediumFileUtf16,
deviceType, accessMode, &medium);
/* The following is not needed for vbox 4.2+ but older versions have
* distinct find and open operations where the former looks in vbox
* media registry while the latter at storage location. In 4.2+, the
* OpenMedium call takes care of both cases internally
*/
if (!medium) {
rc = gVBoxAPI.UIVirtualBox.OpenMedium(data->vboxObj,
mediumFileUtf16,
deviceType, accessMode,
&medium);
}
if (!medium) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Failed to open the following disk/dvd/floppy "
"to the machine: %s, rc=%08x"), src, rc);
ret = -1;
goto cleanup;
}
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, rc);
ret = -1;
goto cleanup;
}
}
if (disk->device == VIR_DOMAIN_DISK_DEVICE_DISK) {
if (disk->src->readonly) {
gVBoxAPI.UIMedium.SetType(medium, MediumType_Immutable);
VIR_DEBUG("Setting hard disk to immutable");
} else if (!disk->src->readonly) {
gVBoxAPI.UIMedium.SetType(medium, MediumType_Normal);
VIR_DEBUG("Setting hard disk type to normal");
}
}
VBOX_UTF16_TO_UTF8(storageCtlName, &controllerName);
VIR_DEBUG("Attaching disk(%zu), controller: %s, port: %d, slot: %d, "
"type: %d, medium: %s", i, controllerName, devicePort,
deviceSlot, deviceType, medium == NULL ? "empty" : src);
VBOX_UTF8_FREE(controllerName);
/* Attach the harddisk/dvd/Floppy to the storage controller,
* medium == NULL is ok here
*/
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, rc);
ret = -1;
goto cleanup;
} else {
DEBUGIID("Attached HDD/DVD/Floppy with UUID", &mediumUUID);
}
cleanup:
VBOX_MEDIUM_RELEASE(medium);
vboxIIDUnalloc(&mediumUUID);
VBOX_UTF16_FREE(mediumFileUtf16);
VBOX_UTF16_FREE(storageCtlName);
if (ret < 0)
break;
}
return ret;
}
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 int
vboxAttachNetwork(virDomainDefPtr def, vboxDriverPtr 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);
if (def->nets[i]->guestIP.nips == 1) {
char *ipStr = virSocketAddrFormat(&def->nets[i]->guestIP.ips[0]->address);
VIR_DEBUG("NIC(%zu): ipaddr: %s", i, ipStr);
VIR_FREE(ipStr);
} else if (def->nets[i]->guestIP.nips > 1) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("Driver does not support setting multiple IP addresses"));
return -1;
}
}
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);
}
return 0;
}
static void
vboxAttachSerial(virDomainDefPtr def, vboxDriverPtr 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, vboxDriverPtr 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,
¶llelPortCount);
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, ¶llelPort);
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) {
if (def->videos[0]->accel->accel3d) {
gVBoxAPI.UIMachine.SetAccelerate3DEnabled(machine,
def->videos[0]->accel->accel3d == VIR_TRISTATE_BOOL_YES);
}
if (def->videos[0]->accel->accel2d) {
gVBoxAPI.UIMachine.SetAccelerate2DVideoEnabled(machine,
def->videos[0]->accel->accel2d == VIR_TRISTATE_BOOL_YES);
}
} else {
gVBoxAPI.UIMachine.SetAccelerate3DEnabled(machine, 0);
gVBoxAPI.UIMachine.SetAccelerate2DVideoEnabled(machine, 0);
}
}
}
static void
vboxAttachDisplay(virDomainDefPtr def, vboxDriverPtr data, IMachine *machine)
{
int vrdpPresent = 0;
int sdlPresent = 0;
int guiPresent = 0;
char *guiDisplay = NULL;
char *sdlDisplay = NULL;
size_t i = 0;
virDomainGraphicsListenDefPtr glisten;
for (i = 0; i < def->ngraphics; i++) {
IVRDEServer *VRDEServer = NULL;
if ((def->graphics[i]->type == VIR_DOMAIN_GRAPHICS_TYPE_RDP) &&
(vrdpPresent == 0)) {
vrdpPresent = 1;
gVBoxAPI.UIMachine.GetVRDEServer(machine, &VRDEServer);
if (VRDEServer) {
gVBoxAPI.UIVRDEServer.SetEnabled(VRDEServer, PR_TRUE);
VIR_DEBUG("VRDP Support turned ON.");
gVBoxAPI.UIVRDEServer.SetPorts(data, VRDEServer, def->graphics[i]);
if (def->graphics[i]->data.rdp.replaceUser) {
gVBoxAPI.UIVRDEServer.SetReuseSingleConnection(VRDEServer,
PR_TRUE);
VIR_DEBUG("VRDP set to reuse single connection");
}
if (def->graphics[i]->data.rdp.multiUser) {
gVBoxAPI.UIVRDEServer.SetAllowMultiConnection(VRDEServer,
PR_TRUE);
VIR_DEBUG("VRDP set to allow multiple connection");
}
if ((glisten = virDomainGraphicsGetListen(def->graphics[i], 0)) &&
glisten->address) {
PRUnichar *netAddressUtf16 = NULL;
VBOX_UTF8_TO_UTF16(glisten->address, &netAddressUtf16);
gVBoxAPI.UIVRDEServer.SetNetAddress(data, VRDEServer,
netAddressUtf16);
VIR_DEBUG("VRDP listen address is set to: %s",
glisten->address);
VBOX_UTF16_FREE(netAddressUtf16);
}
VBOX_RELEASE(VRDEServer);
}
}
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, vboxDriverPtr 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, vboxDriverPtr 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->path, &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
vboxDomainDefineXMLFlags(virConnectPtr conn, const char *xml, unsigned int flags)
{
vboxDriverPtr data = conn->privateData;
IMachine *machine = NULL;
IBIOSSettings *bios = NULL;
vboxIID mchiid;
virDomainDefPtr def = NULL;
nsresult rc;
char uuidstr[VIR_UUID_STRING_BUFLEN];
virDomainPtr ret = NULL;
unsigned int parse_flags = VIR_DOMAIN_DEF_PARSE_INACTIVE;
bool machineReady = false;
virCheckFlags(VIR_DOMAIN_DEFINE_VALIDATE, NULL);
if (flags & VIR_DOMAIN_DEFINE_VALIDATE)
parse_flags |= VIR_DOMAIN_DEF_PARSE_VALIDATE_SCHEMA;
if (!data->vboxObj)
return ret;
if (!(def = virDomainDefParseString(xml, data->caps, data->xmlopt,
NULL, parse_flags)))
return ret;
VBOX_IID_INITIALIZE(&mchiid);
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 (virDomainDefHasVcpusOffline(def)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("current vcpu count must equal maximum"));
}
rc = gVBoxAPI.UIMachine.SetCPUCount(machine, virDomainDefGetVcpusMax(def));
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("could not set the number of virtual CPUs to: %u, rc=%08x"),
virDomainDefGetVcpusMax(def), (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);
if (vboxAttachStorageControllers(def, data, machine) < 0)
goto cleanup;
if (vboxAttachDrives(def, data, machine) < 0)
goto cleanup;
vboxAttachSound(def, machine);
if (vboxAttachNetwork(def, data, machine) < 0)
goto cleanup;
vboxAttachSerial(def, data, machine);
vboxAttachParallel(def, data, machine);
vboxAttachVideo(def, machine);
vboxAttachDisplay(def, data, machine);
vboxAttachUSB(def, data, machine);
vboxAttachSharedFolder(def, data, machine);
machineReady = true;
cleanup:
/* if machine wasn't even created, cleanup is trivial */
if (!machine) {
vboxIIDUnalloc(&mchiid);
virDomainDefFree(def);
return ret;
}
/* Save the machine settings made till now, even when jumped here on error,
* as otherwise unregister won't cleanup properly. For example, it won't
* close media that were partially attached. The VBOX SDK docs say that
* unregister implicitly calls saveSettings but evidently it's not so...
*/
rc = gVBoxAPI.UIMachine.SaveSettings(machine);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Failed to save VM settings, rc=%08x"), rc);
machineReady = false;
}
gVBoxAPI.UISession.Close(data->vboxSession);
if (machineReady) {
ret = virGetDomain(conn, def->name, def->uuid, -1);
} else {
/* Unregister incompletely configured VM to not leave garbage behind */
rc = gVBoxAPI.unregisterMachine(data, &mchiid, &machine);
if (NS_SUCCEEDED(rc))
gVBoxAPI.deleteConfig(machine);
else
VIR_WARN("Could not cleanup partially created VM after failure, "
"rc=%08x", rc);
}
VBOX_RELEASE(machine);
vboxIIDUnalloc(&mchiid);
virDomainDefFree(def);
return ret;
}
static virDomainPtr
vboxDomainDefineXML(virConnectPtr conn, const char *xml)
{
return vboxDomainDefineXMLFlags(conn, xml, 0);
}
static int vboxDomainUndefineFlags(virDomainPtr dom, unsigned int flags)
{
vboxDriverPtr data = dom->conn->privateData;
IMachine *machine = NULL;
vboxIID iid;
nsresult rc;
int ret = -1;
if (!data->vboxObj)
return ret;
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);
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, vboxIID *iid)
{
vboxDriverPtr data = dom->conn->privateData;
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;
int ret = -1;
if (!data->vboxObj)
return ret;
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)
{
vboxDriverPtr data = dom->conn->privateData;
vboxArray machines = VBOX_ARRAY_INITIALIZER;
unsigned char uuid[VIR_UUID_BUFLEN] = {0};
nsresult rc;
size_t i = 0;
int ret = -1;
if (!data->vboxObj)
return ret;
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) {
vboxIID 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)
{
vboxDriverPtr data = dom->conn->privateData;
vboxArray machines = VBOX_ARRAY_INITIALIZER;
vboxIID iid;
char *machineNameUtf8 = NULL;
PRUnichar *machineNameUtf16 = NULL;
unsigned char uuid[VIR_UUID_BUFLEN];
size_t i;
bool matched = false;
nsresult rc;
int ret = -1;
if (!data->vboxObj)
return ret;
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. */
vboxDriverPtr data = dom->conn->privateData;
vboxIID iid;
IMachine *machine = NULL;
int ret = -1;
if (!data->vboxObj)
return ret;
if (openSessionForMachine(data, dom->uuid, &iid, &machine) < 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. */
vboxDriverPtr data = dom->conn->privateData;
vboxIID iid;
IMachine *machine = NULL;
int ret = -1;
if (!data->vboxObj)
return ret;
if (openSessionForMachine(data, dom->uuid, &iid, &machine) < 0)
goto cleanup;
ret = 0;
cleanup:
VBOX_RELEASE(machine);
vboxIIDUnalloc(&iid);
return ret;
}
static int vboxDomainSuspend(virDomainPtr dom)
{
vboxDriverPtr data = dom->conn->privateData;
IMachine *machine = NULL;
vboxIID iid;
IConsole *console = NULL;
PRBool isAccessible = PR_FALSE;
PRUint32 state;
int ret = -1;
if (!data->vboxObj)
return ret;
if (openSessionForMachine(data, dom->uuid, &iid, &machine) < 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)
{
vboxDriverPtr data = dom->conn->privateData;
IMachine *machine = NULL;
vboxIID iid;
IConsole *console = NULL;
PRUint32 state;
PRBool isAccessible = PR_FALSE;
int ret = -1;
if (!data->vboxObj)
return ret;
if (openSessionForMachine(data, dom->uuid, &iid, &machine) < 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)
{
vboxDriverPtr data = dom->conn->privateData;
IMachine *machine = NULL;
vboxIID iid;
IConsole *console = NULL;
PRUint32 state;
PRBool isAccessible = PR_FALSE;
int ret = -1;
if (!data->vboxObj)
return ret;
virCheckFlags(0, -1);
if (openSessionForMachine(data, dom->uuid, &iid, &machine) < 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)
{
vboxDriverPtr data = dom->conn->privateData;
IMachine *machine = NULL;
vboxIID iid;
IConsole *console = NULL;
PRUint32 state;
PRBool isAccessible = PR_FALSE;
int ret = -1;
if (!data->vboxObj)
return ret;
virCheckFlags(0, -1);
if (openSessionForMachine(data, dom->uuid, &iid, &machine) < 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)
{
vboxDriverPtr data = dom->conn->privateData;
IMachine *machine = NULL;
vboxIID iid;
IConsole *console = NULL;
PRUint32 state;
PRBool isAccessible = PR_FALSE;
int ret = -1;
if (!data->vboxObj)
return ret;
virCheckFlags(0, -1);
if (openSessionForMachine(data, dom->uuid, &iid, &machine) < 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)
{
vboxDriverPtr data = dom->conn->privateData;
IMachine *machine = NULL;
vboxIID iid;
PRUint32 state;
PRBool isAccessible = PR_FALSE;
nsresult rc;
int ret = -1;
if (!data->vboxObj)
return ret;
if (openSessionForMachine(data, dom->uuid, &iid, &machine) < 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)
{
vboxDriverPtr data = dom->conn->privateData;
vboxArray machines = VBOX_ARRAY_INITIALIZER;
char *machineName = NULL;
PRUnichar *machineNameUtf16 = NULL;
nsresult rc;
size_t i = 0;
int ret = -1;
if (!data->vboxObj)
return ret;
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)
{
vboxDriverPtr data = dom->conn->privateData;
vboxIID domiid;
IMachine *machine = NULL;
PRUint32 mstate;
int ret = -1;
if (!data->vboxObj)
return ret;
virCheckFlags(0, -1);
if (openSessionForMachine(data, dom->uuid, &domiid, &machine) < 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)
{
vboxDriverPtr data = dom->conn->privateData;
IMachine *machine = NULL;
vboxIID iid;
PRUint32 CPUCount = nvcpus;
nsresult rc;
int ret = -1;
if (!data->vboxObj)
return ret;
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) < 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)
{
vboxDriverPtr data = dom->conn->privateData;
ISystemProperties *systemProperties = NULL;
PRUint32 maxCPUCount = 0;
int ret = -1;
if (!data->vboxObj)
return ret;
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(vboxDriverPtr 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] = virDomainHostdevDefNew();
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 int
vboxDumpStorageControllers(virDomainDefPtr def, IMachine *machine)
{
vboxArray storageControllers = VBOX_ARRAY_INITIALIZER;
IStorageController *controller = NULL;
PRUint32 storageBus = StorageBus_Null;
PRUint32 controllerType = StorageControllerType_Null;
virDomainControllerDefPtr cont = NULL;
size_t i = 0;
int model = -1, ret = -1;
virDomainControllerType type = VIR_DOMAIN_CONTROLLER_TYPE_LAST;
gVBoxAPI.UArray.vboxArrayGet(&storageControllers, machine,
gVBoxAPI.UArray.handleMachineGetStorageControllers(machine));
for (i = 0; i < storageControllers.count; i++) {
controller = storageControllers.items[i];
storageBus = StorageBus_Null;
controllerType = StorageControllerType_Null;
type = VIR_DOMAIN_CONTROLLER_TYPE_LAST;
model = -1;
if (!controller)
continue;
gVBoxAPI.UIStorageController.GetBus(controller, &storageBus);
gVBoxAPI.UIStorageController.GetControllerType(controller,
&controllerType);
/* vbox controller model => libvirt controller model */
switch ((enum StorageControllerType) controllerType) {
case StorageControllerType_PIIX3:
model = VIR_DOMAIN_CONTROLLER_MODEL_IDE_PIIX3;
break;
case StorageControllerType_PIIX4:
model = VIR_DOMAIN_CONTROLLER_MODEL_IDE_PIIX4;
break;
case StorageControllerType_ICH6:
model = VIR_DOMAIN_CONTROLLER_MODEL_IDE_ICH6;
break;
case StorageControllerType_BusLogic:
model = VIR_DOMAIN_CONTROLLER_MODEL_SCSI_BUSLOGIC;
break;
case StorageControllerType_LsiLogic:
model = VIR_DOMAIN_CONTROLLER_MODEL_SCSI_LSILOGIC;
break;
case StorageControllerType_LsiLogicSas:
model = VIR_DOMAIN_CONTROLLER_MODEL_SCSI_LSISAS1068;
break;
case StorageControllerType_IntelAhci:
case StorageControllerType_I82078:
case StorageControllerType_Null:
model = -1;
break;
}
/* vbox controller bus => libvirt controller type */
switch ((enum StorageBus) storageBus) {
case StorageBus_IDE:
type = VIR_DOMAIN_CONTROLLER_TYPE_IDE;
break;
case StorageBus_SCSI:
case StorageBus_SAS:
type = VIR_DOMAIN_CONTROLLER_TYPE_SCSI;
break;
case StorageBus_SATA:
type = VIR_DOMAIN_CONTROLLER_TYPE_SATA;
break;
case StorageBus_Floppy:
type = VIR_DOMAIN_CONTROLLER_TYPE_FDC;
break;
case StorageBus_Null:
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Unsupported null storage bus"));
goto cleanup;
}
if (type != VIR_DOMAIN_CONTROLLER_TYPE_LAST) {
cont = virDomainDefAddController(def, type, -1, model);
if (!cont) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Failed to add %s controller type definition"),
virDomainControllerTypeToString(type));
goto cleanup;
}
}
}
ret = 0;
cleanup:
gVBoxAPI.UArray.vboxArrayRelease(&storageControllers);
return ret;
}
static int
vboxDumpDisks(virDomainDefPtr def, vboxDriverPtr data, IMachine *machine)
{
vboxArray mediumAttachments = VBOX_ARRAY_INITIALIZER;
int ret = -1;
IMediumAttachment *mediumAttachment = NULL;
IMedium *medium = NULL;
IStorageController *controller = NULL;
PRUnichar *controllerName = NULL, *mediumLocUtf16 = NULL;
PRUint32 deviceType, storageBus;
PRInt32 devicePort, deviceSlot;
PRBool readOnly;
nsresult rc;
virDomainDiskDefPtr disk = NULL;
virDomainControllerDefPtr ctrl = NULL;
char *mediumLocUtf8 = NULL;
size_t sdCount = 0, i, j;
def->ndisks = 0;
gVBoxAPI.UArray.vboxArrayGet(&mediumAttachments, machine,
gVBoxAPI.UArray.handleMachineGetMediumAttachments(machine));
/* get the number of attachments */
for (i = 0; i < mediumAttachments.count; i++) {
mediumAttachment = mediumAttachments.items[i];
if (!mediumAttachment)
continue;
rc = gVBoxAPI.UIMediumAttachment.GetMedium(mediumAttachment, &medium);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Could not get IMedium, rc=%08x"), rc);
goto cleanup;
}
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++) {
disk = virDomainDiskDefNew(NULL);
if (!disk)
goto cleanup;
def->disks[i] = disk;
}
/* get the attachment details here */
for (i = 0; i < mediumAttachments.count; i++) {
mediumAttachment = mediumAttachments.items[i];
controller = NULL;
controllerName = NULL;
deviceType = DeviceType_Null;
storageBus = StorageBus_Null;
readOnly = PR_FALSE;
medium = NULL;
mediumLocUtf16 = NULL;
mediumLocUtf8 = NULL;
devicePort = 0;
deviceSlot = 0;
disk = def->disks[i];
if (!mediumAttachment)
continue;
rc = gVBoxAPI.UIMediumAttachment.GetMedium(mediumAttachment, &medium);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Could not get IMedium, rc=%08x"), rc);
goto cleanup;
}
rc = gVBoxAPI.UIMediumAttachment.GetController(mediumAttachment,
&controllerName);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Failed to get storage controller name, rc=%08x"),
rc);
goto cleanup;
}
rc = gVBoxAPI.UIMachine.GetStorageControllerByName(machine,
controllerName,
&controller);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Could not get storage controller by name, rc=%08x"),
rc);
goto cleanup;
}
rc = gVBoxAPI.UIMediumAttachment.GetType(mediumAttachment, &deviceType);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Could not get device type, rc=%08x"), rc);
goto cleanup;
}
rc = gVBoxAPI.UIMediumAttachment.GetPort(mediumAttachment, &devicePort);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Could not get device port, rc=%08x"), rc);
goto cleanup;
}
rc = gVBoxAPI.UIMediumAttachment.GetDevice(mediumAttachment, &deviceSlot);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Could not get device slot, rc=%08x"), rc);
goto cleanup;
}
rc = gVBoxAPI.UIStorageController.GetBus(controller, &storageBus);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Could not get storage controller bus, rc=%08x"),
rc);
goto cleanup;
}
if (medium) {
rc = gVBoxAPI.UIMedium.GetLocation(medium, &mediumLocUtf16);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Could not get medium storage location, rc=%08x"),
rc);
goto cleanup;
}
VBOX_UTF16_TO_UTF8(mediumLocUtf16, &mediumLocUtf8);
if (virDomainDiskSetSource(disk, mediumLocUtf8) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Could not set disk source"));
goto cleanup;
}
rc = gVBoxAPI.UIMedium.GetReadOnly(medium, &readOnly);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Could not get read only state, rc=%08x"), rc);
goto cleanup;
}
}
disk->dst = vboxGenerateMediumName(storageBus, devicePort, deviceSlot,
sdCount);
if (!disk->dst) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Could not generate medium name for the disk "
"at: port:%d, slot:%d"), devicePort, deviceSlot);
goto cleanup;
}
disk->info.type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DRIVE;
disk->info.addr.drive.bus = 0;
disk->info.addr.drive.unit = devicePort;
switch ((enum StorageBus) storageBus) {
case StorageBus_IDE:
disk->bus = VIR_DOMAIN_DISK_BUS_IDE;
disk->info.addr.drive.bus = devicePort; /* primary, secondary */
disk->info.addr.drive.unit = deviceSlot; /* master, slave */
break;
case StorageBus_SATA:
disk->bus = VIR_DOMAIN_DISK_BUS_SATA;
sdCount++;
break;
case StorageBus_SCSI:
case StorageBus_SAS:
disk->bus = VIR_DOMAIN_DISK_BUS_SCSI;
sdCount++;
/* In vbox, if there's a disk attached to SAS controller, there will
* be libvirt SCSI controller present with model "lsi1068", and we
* need to find its index
*/
for (j = 0; j < def->ncontrollers; j++) {
ctrl = def->controllers[j];
if (ctrl->type != VIR_DOMAIN_CONTROLLER_TYPE_SCSI)
continue;
if (storageBus == StorageBus_SAS &&
ctrl->model == VIR_DOMAIN_CONTROLLER_MODEL_SCSI_LSISAS1068) {
disk->info.addr.drive.controller = ctrl->idx;
break;
}
if (storageBus == StorageBus_SCSI &&
ctrl->model != VIR_DOMAIN_CONTROLLER_MODEL_SCSI_LSISAS1068) {
disk->info.addr.drive.controller = ctrl->idx;
break;
}
}
break;
case StorageBus_Floppy:
disk->bus = VIR_DOMAIN_DISK_BUS_FDC;
break;
case StorageBus_Null:
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Unsupported null storage bus"));
goto cleanup;
}
switch ((enum DeviceType) deviceType) {
case DeviceType_HardDisk:
disk->device = VIR_DOMAIN_DISK_DEVICE_DISK;
break;
case DeviceType_Floppy:
disk->device = VIR_DOMAIN_DISK_DEVICE_FLOPPY;
break;
case DeviceType_DVD:
disk->device = VIR_DOMAIN_DISK_DEVICE_CDROM;
break;
case DeviceType_Network:
case DeviceType_USB:
case DeviceType_SharedFolder:
case DeviceType_Null:
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Unsupported vbox device type: %d"), deviceType);
goto cleanup;
}
if (readOnly == PR_TRUE)
disk->src->readonly = true;
virDomainDiskSetType(disk, VIR_STORAGE_TYPE_FILE);
VBOX_UTF16_FREE(controllerName);
VBOX_UTF8_FREE(mediumLocUtf8);
VBOX_UTF16_FREE(mediumLocUtf16);
VBOX_RELEASE(medium);
VBOX_RELEASE(controller);
}
ret = 0;
cleanup:
gVBoxAPI.UArray.vboxArrayRelease(&mediumAttachments);
if (ret < 0) {
VBOX_UTF16_FREE(controllerName);
VBOX_UTF8_FREE(mediumLocUtf8);
VBOX_UTF16_FREE(mediumLocUtf16);
VBOX_RELEASE(medium);
VBOX_RELEASE(controller);
}
return ret;
}
static int
vboxDumpVideo(virDomainDefPtr def, vboxDriverPtr data ATTRIBUTE_UNUSED,
IMachine *machine)
{
/* dump video options vram/2d/3d/directx/etc. */
/* the default is: vram is 8MB, One monitor, 3dAccel Off */
PRUint32 VRAMSize = 8;
PRUint32 monitorCount = 1;
PRBool accelerate3DEnabled = PR_FALSE;
PRBool accelerate2DEnabled = PR_FALSE;
/* Currently supports only one graphics card */
if (VIR_ALLOC_N(def->videos, 1) < 0)
return -1;
def->nvideos = 1;
if (VIR_ALLOC(def->videos[0]) < 0)
return -1;
gVBoxAPI.UIMachine.GetVRAMSize(machine, &VRAMSize);
gVBoxAPI.UIMachine.GetMonitorCount(machine, &monitorCount);
gVBoxAPI.UIMachine.GetAccelerate3DEnabled(machine, &accelerate3DEnabled);
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)
return -1;
def->videos[0]->accel->accel3d = accelerate3DEnabled ?
VIR_TRISTATE_BOOL_YES : VIR_TRISTATE_BOOL_NO;
def->videos[0]->accel->accel2d = accelerate2DEnabled ?
VIR_TRISTATE_BOOL_YES : VIR_TRISTATE_BOOL_NO;
return 0;
}
static int
vboxDumpDisplay(virDomainDefPtr def, vboxDriverPtr data, IMachine *machine)
{
/* dump display options vrdp/gui/sdl */
PRUnichar *keyUtf16 = NULL;
PRUnichar *valueTypeUtf16 = NULL;
char *valueTypeUtf8 = NULL;
char *netAddressUtf8 = NULL;
IVRDEServer *VRDEServer = NULL;
PRBool VRDxEnabled = PR_FALSE;
virDomainGraphicsDefPtr graphics = NULL;
int ret = -1;
def->ngraphics = 0;
VBOX_UTF8_TO_UTF16("FRONTEND/Type", &keyUtf16);
gVBoxAPI.UIMachine.GetExtraData(machine, keyUtf16, &valueTypeUtf16);
VBOX_UTF16_FREE(keyUtf16);
if (valueTypeUtf16) {
VBOX_UTF16_TO_UTF8(valueTypeUtf16, &valueTypeUtf8);
VBOX_UTF16_FREE(valueTypeUtf16);
}
if (STREQ_NULLABLE(valueTypeUtf8, "sdl") ||
STREQ_NULLABLE(valueTypeUtf8, "gui")) {
PRUnichar *valueDisplayUtf16 = NULL;
char *valueDisplayUtf8 = NULL;
if (VIR_ALLOC(graphics) < 0)
goto cleanup;
VBOX_UTF8_TO_UTF16("FRONTEND/Display", &keyUtf16);
gVBoxAPI.UIMachine.GetExtraData(machine, keyUtf16, &valueDisplayUtf16);
VBOX_UTF16_FREE(keyUtf16);
if (valueDisplayUtf16) {
VBOX_UTF16_TO_UTF8(valueDisplayUtf16, &valueDisplayUtf8);
VBOX_UTF16_FREE(valueDisplayUtf16);
if (STREQ(valueDisplayUtf8, ""))
VBOX_UTF8_FREE(valueDisplayUtf8);
}
if (STREQ(valueTypeUtf8, "sdl")) {
graphics->type = VIR_DOMAIN_GRAPHICS_TYPE_SDL;
graphics->data.sdl.display = valueDisplayUtf8;
valueDisplayUtf8 = NULL;
}
if (STREQ(valueTypeUtf8, "gui")) {
graphics->type = VIR_DOMAIN_GRAPHICS_TYPE_DESKTOP;
graphics->data.desktop.display = valueDisplayUtf8;
valueDisplayUtf8 = NULL;
}
VBOX_UTF8_FREE(valueDisplayUtf8);
} else if (STRNEQ_NULLABLE(valueTypeUtf8, "vrdp")) {
if (VIR_ALLOC(graphics) < 0)
goto cleanup;
graphics->type = VIR_DOMAIN_GRAPHICS_TYPE_DESKTOP;
if (VIR_STRDUP(graphics->data.desktop.display,
virGetEnvBlockSUID("DISPLAY")) < 0)
goto cleanup;
}
if (graphics &&
VIR_APPEND_ELEMENT(def->graphics, def->ngraphics, graphics) < 0)
goto cleanup;
gVBoxAPI.UIMachine.GetVRDEServer(machine, &VRDEServer);
if (VRDEServer)
gVBoxAPI.UIVRDEServer.GetEnabled(VRDEServer, &VRDxEnabled);
if (VRDxEnabled) {
PRUnichar *netAddressUtf16 = NULL;
PRBool allowMultiConnection = PR_FALSE;
PRBool reuseSingleConnection = PR_FALSE;
if (VIR_ALLOC(graphics) < 0)
goto cleanup;
gVBoxAPI.UIVRDEServer.GetPorts(data, VRDEServer, machine, graphics);
graphics->type = VIR_DOMAIN_GRAPHICS_TYPE_RDP;
gVBoxAPI.UIVRDEServer.GetNetAddress(data, VRDEServer, &netAddressUtf16);
if (netAddressUtf16) {
VBOX_UTF16_TO_UTF8(netAddressUtf16, &netAddressUtf8);
VBOX_UTF16_FREE(netAddressUtf16);
}
if (netAddressUtf8 && STREQ(netAddressUtf8, ""))
VBOX_UTF8_FREE(netAddressUtf8);
if (virDomainGraphicsListenAppendAddress(graphics, netAddressUtf8) < 0)
goto cleanup;
gVBoxAPI.UIVRDEServer.GetAllowMultiConnection(VRDEServer, &allowMultiConnection);
if (allowMultiConnection)
graphics->data.rdp.multiUser = true;
gVBoxAPI.UIVRDEServer.GetReuseSingleConnection(VRDEServer, &reuseSingleConnection);
if (reuseSingleConnection)
graphics->data.rdp.replaceUser = true;
if (VIR_APPEND_ELEMENT(def->graphics, def->ngraphics, graphics) < 0)
goto cleanup;
}
ret = 0;
cleanup:
VBOX_RELEASE(VRDEServer);
VBOX_UTF8_FREE(valueTypeUtf8);
VBOX_UTF8_FREE(netAddressUtf8);
virDomainGraphicsDefFree(graphics);
return ret;
}
static int
vboxDumpSharedFolders(virDomainDefPtr def, vboxDriverPtr data, IMachine *machine)
{
vboxArray sharedFolders = VBOX_ARRAY_INITIALIZER;
size_t i = 0;
int ret = -1;
def->nfss = 0;
gVBoxAPI.UArray.vboxArrayGet(&sharedFolders, machine,
gVBoxAPI.UArray.handleMachineGetSharedFolders(machine));
if (sharedFolders.count <= 0) {
if (sharedFolders.count == 0)
ret = 0;
goto cleanup;
}
if (VIR_ALLOC_N(def->fss, sharedFolders.count) < 0)
goto cleanup;
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 cleanup;
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->path, hostPath) < 0) {
VBOX_UTF8_FREE(hostPath);
VBOX_UTF16_FREE(hostPathUtf16);
goto cleanup;
}
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 cleanup;
}
VBOX_UTF8_FREE(name);
VBOX_UTF16_FREE(nameUtf16);
gVBoxAPI.UISharedFolder.GetWritable(sharedFolder, &writable);
def->fss[i]->readonly = !writable;
++def->nfss;
}
ret = 0;
cleanup:
gVBoxAPI.UArray.vboxArrayRelease(&sharedFolders);
return ret;
}
static virDomainNetDefPtr
vboxDumpNetwork(vboxDriverPtr data, INetworkAdapter *adapter)
{
PRUint32 attachmentType = NetworkAttachmentType_Null;
PRUint32 adapterType = NetworkAdapterType_Null;
const char *model = NULL;
PRUnichar *utf16 = NULL;
char *utf8 = NULL;
virDomainNetDefPtr net = NULL;
if (VIR_ALLOC(net) < 0)
return NULL;
gVBoxAPI.UINetworkAdapter.GetAttachmentType(adapter, &attachmentType);
switch (attachmentType) {
case NetworkAttachmentType_NAT:
net->type = VIR_DOMAIN_NET_TYPE_USER;
break;
case NetworkAttachmentType_Bridged:
net->type = VIR_DOMAIN_NET_TYPE_BRIDGE;
gVBoxAPI.UINetworkAdapter.GetBridgedInterface(adapter, &utf16);
VBOX_UTF16_TO_UTF8(utf16, &utf8);
VIR_STEAL_PTR(net->data.bridge.brname, utf8);
VBOX_UTF16_FREE(utf16);
break;
case NetworkAttachmentType_Internal:
net->type = VIR_DOMAIN_NET_TYPE_INTERNAL;
gVBoxAPI.UINetworkAdapter.GetInternalNetwork(adapter, &utf16);
VBOX_UTF16_TO_UTF8(utf16, &utf8);
VIR_STEAL_PTR(net->data.internal.name, utf8);
VBOX_UTF16_FREE(utf16);
break;
case NetworkAttachmentType_HostOnly:
net->type = VIR_DOMAIN_NET_TYPE_NETWORK;
gVBoxAPI.UINetworkAdapter.GetHostOnlyInterface(adapter, &utf16);
VBOX_UTF16_TO_UTF8(utf16, &utf8);
VIR_STEAL_PTR(net->data.network.name, utf8);
VBOX_UTF16_FREE(utf16);
break;
default:
/* default to user type i.e. NAT in VirtualBox if this
* dump is ever used to create a machine.
*/
net->type = VIR_DOMAIN_NET_TYPE_USER;
}
gVBoxAPI.UINetworkAdapter.GetAdapterType(adapter, &adapterType);
switch (adapterType) {
case NetworkAdapterType_Am79C970A:
model = "Am79C970A";
break;
case NetworkAdapterType_Am79C973:
model = "Am79C973";
break;
case NetworkAdapterType_I82540EM:
model = "82540EM";
break;
case NetworkAdapterType_I82545EM:
model = "82545EM";
break;
case NetworkAdapterType_I82543GC:
model = "82543GC";
break;
case NetworkAdapterType_Virtio:
/* Only vbox 3.1 and later support NetworkAdapterType_Virto */
if (gVBoxAPI.APIVersion >= 3000051)
model = "virtio";
break;
}
if (VIR_STRDUP(net->model, model) < 0)
goto error;
gVBoxAPI.UINetworkAdapter.GetMACAddress(adapter, &utf16);
VBOX_UTF16_TO_UTF8(utf16, &utf8);
VBOX_UTF16_FREE(utf16);
if (virMacAddrParseHex(utf8, &net->mac) < 0) {
VBOX_UTF8_FREE(utf8);
goto error;
}
VBOX_UTF8_FREE(utf8);
return net;
error:
virDomainNetDefFree(net);
return NULL;
}
static int
vboxDumpNetworks(virDomainDefPtr def, vboxDriverPtr data, IMachine *machine, PRUint32 networkAdapterCount)
{
size_t i = 0;
for (i = 0; i < networkAdapterCount; i++) {
INetworkAdapter *adapter = NULL;
virDomainNetDefPtr net = NULL;
PRBool enabled = PR_FALSE;
gVBoxAPI.UIMachine.GetNetworkAdapter(machine, i, &adapter);
if (adapter)
gVBoxAPI.UINetworkAdapter.GetEnabled(adapter, &enabled);
if (enabled) {
net = vboxDumpNetwork(data, adapter);
if (VIR_APPEND_ELEMENT(def->nets, def->nnets, net) < 0) {
VBOX_RELEASE(adapter);
return -1;
}
}
VBOX_RELEASE(adapter);
}
return 0;
}
static void
vboxDumpAudio(virDomainDefPtr def, vboxDriverPtr 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 int
vboxDumpSerial(virDomainDefPtr def, vboxDriverPtr 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) {
if (VIR_ALLOC_N(def->serials, def->nserials) < 0)
return -1;
for (i = 0; i < def->nserials; i++) {
def->serials[i] = virDomainChrDefNew(NULL);
if (!def->serials[i])
return -1;
}
}
/* 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);
if (VIR_STRDUP(def->serials[serialPortIncCount]->source->data.file.path, path) < 0)
return -1;
}
serialPortIncCount++;
VBOX_UTF16_FREE(pathUtf16);
VBOX_UTF8_FREE(path);
}
VBOX_RELEASE(serialPort);
}
}
return 0;
}
static int
vboxDumpParallel(virDomainDefPtr def, vboxDriverPtr 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, ¶llelPort);
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) {
if (VIR_ALLOC_N(def->parallels, def->nparallels) < 0)
return -1;
for (i = 0; i < def->nparallels; i++) {
def->parallels[i] = virDomainChrDefNew(NULL);
if (!def->parallels[i])
return -1;
}
}
/* 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, ¶llelPort);
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);
if (VIR_STRDUP(def->parallels[parallelPortIncCount]->source->data.file.path, path) < 0)
return -1;
parallelPortIncCount++;
VBOX_UTF16_FREE(pathUtf16);
VBOX_UTF8_FREE(path);
}
VBOX_RELEASE(parallelPort);
}
}
return 0;
}
static char *vboxDomainGetXMLDesc(virDomainPtr dom, unsigned int flags)
{
vboxDriverPtr data = dom->conn->privateData;
virDomainDefPtr def = NULL;
IMachine *machine = NULL;
vboxIID 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;
char *ret = NULL;
if (!data->vboxObj)
return ret;
/* Flags checked by virDomainDefFormat */
if (openSessionForMachine(data, dom->uuid, &iid, &machine) < 0)
goto cleanup;
if (!(def = virDomainDefNew()))
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, ¶llelPortCount);
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; */
virDomainDefSetMemoryTotal(def, memorySize * 1024);
gVBoxAPI.UIMachine.GetCPUCount(machine, &CPUCount);
if (virDomainDefSetVcpusMax(def, CPUCount, data->xmlopt) < 0)
goto cleanup;
if (virDomainDefSetVcpus(def, CPUCount) < 0)
goto cleanup;
/* Skip cpumasklen, cpumask, onReboot, onPoweroff, onCrash */
def->os.type = VIR_DOMAIN_OSTYPE_HVM;
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;
if (vboxDumpVideo(def, data, machine) < 0)
goto cleanup;
if (vboxDumpDisplay(def, data, machine) < 0)
goto cleanup;
if (vboxDumpStorageControllers(def, machine) < 0)
goto cleanup;
if (vboxDumpDisks(def, data, machine) < 0)
goto cleanup;
if (vboxDumpSharedFolders(def, data, machine) < 0)
goto cleanup;
if (vboxDumpNetworks(def, data, machine, networkAdapterCount) < 0)
goto cleanup;
vboxDumpAudio(def, data, machine);
if (vboxDumpSerial(def, data, machine, serialPortCount) < 0)
goto cleanup;
if (vboxDumpParallel(def, data, machine, parallelPortCount) < 0)
goto cleanup;
/* dump USB devices/filters if active */
vboxHostDeviceGetXMLDesc(data, def, machine);
ret = virDomainDefFormat(def, data->caps,
virDomainDefFormatConvertXMLFlags(flags));
cleanup:
VBOX_RELEASE(machine);
vboxIIDUnalloc(&iid);
virDomainDefFree(def);
return ret;
}
static int vboxConnectListDefinedDomains(virConnectPtr conn,
char ** const names, int maxnames)
{
vboxDriverPtr data = conn->privateData;
vboxArray machines = VBOX_ARRAY_INITIALIZER;
char *machineName = NULL;
PRUnichar *machineNameUtf16 = NULL;
PRUint32 state;
nsresult rc;
size_t i, j;
int ret = -1;
if (!data->vboxObj)
return ret;
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)
{
vboxDriverPtr data = conn->privateData;
vboxArray machines = VBOX_ARRAY_INITIALIZER;
PRUint32 state;
nsresult rc;
size_t i;
int ret = -1;
if (!data->vboxObj)
return ret;
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)
{
vboxDriverPtr data = dom->conn->privateData;
IMachine *machine = NULL;
vboxIID iid;
PRUint32 state;
virDomainDefPtr def = NULL;
virDomainDeviceDefPtr dev = NULL;
nsresult rc;
int ret = -1;
if (!data->vboxObj)
return ret;
VBOX_IID_INITIALIZE(&iid);
if (!(def = virDomainDefNew()))
return ret;
def->os.type = VIR_DOMAIN_OSTYPE_HVM;
dev = virDomainDeviceDefParse(xml, def, data->caps, data->xmlopt,
VIR_DOMAIN_DEF_PARSE_INACTIVE);
if (dev == NULL)
goto cleanup;
if (openSessionForMachine(data, dom->uuid, &iid, &machine) < 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_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->path, &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)
{
vboxDriverPtr data = dom->conn->privateData;
IMachine *machine = NULL;
vboxIID iid;
PRUint32 state;
virDomainDefPtr def = NULL;
virDomainDeviceDefPtr dev = NULL;
nsresult rc;
int ret = -1;
if (!data->vboxObj)
return ret;
VBOX_IID_INITIALIZE(&iid);
if (!(def = virDomainDefNew()))
return ret;
def->os.type = VIR_DOMAIN_OSTYPE_HVM;
dev = virDomainDeviceDefParse(xml, def, data->caps, data->xmlopt,
VIR_DOMAIN_DEF_PARSE_INACTIVE |
VIR_DOMAIN_DEF_PARSE_SKIP_VALIDATE);
if (dev == NULL)
goto cleanup;
if (openSessionForMachine(data, dom->uuid, &iid, &machine) < 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_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)
{
vboxDriverPtr data = dom->conn->privateData;
nsresult rc;
size_t i = 0;
PRUnichar *locationUtf = NULL;
IMedium *medium = NULL;
IMedium **children = NULL;
PRUint32 childrenSize = 0;
int ret = -1;
if (!data->vboxObj)
return ret;
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.
*/
vboxDriverPtr data = dom->conn->privateData;
vboxIID 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];
char **searchResultTab = NULL;
ssize_t resultSize = 0;
int it = 0;
int jt = 0;
PRUint32 aMediaSize = 0;
IMedium **aMedia = NULL;
char *machineLocationPath = NULL;
char *nameTmpUse = NULL;
bool snapshotFileExists = false;
bool needToChangeStorageController = false;
int ret = -1;
if (!data->vboxObj)
return ret;
if (!gVBoxAPI.vboxSnapshotRedefine)
VIR_WARN("This function may not work in current version");
if (openSessionForMachine(data, dom->uuid, &domiid, &machine) < 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(¤tSnapshotXmlFilePath, "%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;
vboxIID 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 its 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;
vboxIID 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 *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);
virStringListFree(searchResultTab);
searchResultTab = NULL;
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;
vboxIID 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 *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);
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 *tmp = NULL;
vboxIID 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.CreateHardDisk(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);
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(¤tSnapshotXmlFilePath, "%s%s.xml", machineLocationPath, snapshotMachineDesc->currentSnapshot) < 0)
goto cleanup;
char *snapshotContent = virDomainSnapshotDefFormat(NULL, def, data->caps, data->xmlopt, VIR_DOMAIN_DEF_FORMAT_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);
virStringListFree(realReadOnlyDisksPath);
virStringListFree(realReadWriteDisksPath);
virStringListFree(searchResultTab);
VIR_FREE(newSnapshotPtr);
VIR_FREE(machineLocationPath);
VIR_FREE(nameTmpUse);
return ret;
}
static virDomainSnapshotPtr
vboxDomainSnapshotCreateXML(virDomainPtr dom,
const char *xmlDesc,
unsigned int flags)
{
vboxDriverPtr data = dom->conn->privateData;
virDomainSnapshotDefPtr def = NULL;
vboxIID 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;
virDomainSnapshotPtr ret = NULL;
if (!data->vboxObj)
return ret;
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,
VIR_DOMAIN_SNAPSHOT_PARSE_DISKS |
VIR_DOMAIN_SNAPSHOT_PARSE_REDEFINE)))
goto cleanup;
if (openSessionForMachine(data, dom->uuid, &domiid, &machine) < 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)
{
vboxIID 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(vboxDriverPtr data,
virDomainPtr dom,
IMachine *machine,
const char *name)
{
ISnapshot **snapshots = NULL;
ISnapshot *snapshot = NULL;
nsresult rc;
ssize_t i, count = 0;
if ((count = vboxDomainSnapshotGetAll(dom, machine, &snapshots)) < 0)
return NULL;
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:
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;
vboxDriverPtr data = dom->conn->privateData;
vboxIID domiid;
IMachine *machine = NULL;
ISnapshot *snap = NULL;
IMachine *snapMachine = NULL;
vboxArray mediumAttachments = VBOX_ARRAY_INITIALIZER;
size_t diskCount = 0, sdCount = 0;
nsresult rc;
vboxIID snapIid;
char *snapshotUuidStr = NULL;
size_t i = 0;
int ret = -1;
if (!data->vboxObj)
return ret;
if (!gVBoxAPI.vboxSnapshotRedefine)
VIR_WARN("This function may not work in current version");
VBOX_IID_INITIALIZE(&snapIid);
if (openSessionForMachine(data, dom->uuid, &domiid, &machine) < 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;
}
/* 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;
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.GetController(imediumattach,
&storageControllerName);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Cannot get storage controller name"));
goto cleanup;
}
rc = gVBoxAPI.UIMachine.GetStorageControllerByName(machine,
storageControllerName,
&storageController);
VBOX_UTF16_FREE(storageControllerName);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Cannot get storage controller by name"));
goto cleanup;
}
rc = gVBoxAPI.UIStorageController.GetBus(storageController, &storageBus);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Cannot get storage controller bus"));
VBOX_RELEASE(storageController);
goto cleanup;
}
rc = gVBoxAPI.UIMediumAttachment.GetType(imediumattach, &deviceType);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Cannot get medium attachment type"));
VBOX_RELEASE(storageController);
goto cleanup;
}
rc = gVBoxAPI.UIMediumAttachment.GetPort(imediumattach, &devicePort);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Cannot get medium attachment port"));
VBOX_RELEASE(storageController);
goto cleanup;
}
rc = gVBoxAPI.UIMediumAttachment.GetDevice(imediumattach, &deviceSlot);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Cannot get medium attachment slot"));
VBOX_RELEASE(storageController);
goto cleanup;
}
rc = gVBoxAPI.UIMediumAttachment.GetMedium(imediumattach, &disk);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Cannot get medium"));
VBOX_RELEASE(storageController);
goto cleanup;
}
/* skip empty removable disk */
if (!disk) {
/* removable disks with empty (ejected) media won't be displayed
* in XML, but we need to update "sdCount" so that device names match
* in domain dumpxml and snapshot dumpxml
*/
if (storageBus == StorageBus_SATA || storageBus == StorageBus_SCSI ||
storageBus == StorageBus_SAS)
sdCount++;
VBOX_RELEASE(storageController);
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.UIMedium.GetLocation(child, &childLocUtf16);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("cannot get disk location"));
VBOX_RELEASE(storageController);
VBOX_RELEASE(disk);
VBOX_RELEASE(child);
goto cleanup;
}
VBOX_UTF16_TO_UTF8(childLocUtf16, &childLocUtf8);
VBOX_UTF16_FREE(childLocUtf16);
if (VIR_STRDUP(def->disks[diskCount].src->path, childLocUtf8) < 0) {
VBOX_RELEASE(storageController);
VBOX_RELEASE(disk);
VBOX_RELEASE(child);
goto cleanup;
}
VBOX_UTF8_FREE(childLocUtf8);
def->disks[diskCount].src->type = VIR_STORAGE_TYPE_FILE;
def->disks[diskCount].name = vboxGenerateMediumName(storageBus,
devicePort,
deviceSlot,
sdCount);
}
VBOX_UTF8_FREE(diskSnapIdStr);
}
}
VBOX_RELEASE(storageController);
VBOX_RELEASE(disk);
diskCount++;
if (storageBus == StorageBus_SATA || storageBus == StorageBus_SCSI ||
storageBus == StorageBus_SAS)
sdCount++;
}
gVBoxAPI.UArray.vboxArrayRelease(&mediumAttachments);
ret = 0;
cleanup:
VBOX_RELEASE(snap);
return ret;
}
static int
vboxSnapshotGetReadOnlyDisks(virDomainSnapshotDefPtr def,
virDomainSnapshotPtr snapshot)
{
virDomainPtr dom = snapshot->domain;
vboxDriverPtr data = dom->conn->privateData;
vboxIID 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, diskCount = 0, sdCount = 0;
int ret = -1;
if (!data->vboxObj)
return ret;
if (!gVBoxAPI.vboxSnapshotRedefine)
VIR_WARN("This function may not work in current version");
if (openSessionForMachine(data, dom->uuid, &domiid, &machine) < 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 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(NULL);
if (!diskDef)
goto cleanup;
def->dom->disks[i] = diskDef;
}
} else {
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;
PRInt32 devicePort = 0;
PRInt32 deviceSlot = 0;
IMediumAttachment *imediumattach = mediumAttachments.items[i];
if (!imediumattach)
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);
VBOX_UTF16_FREE(storageControllerName);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Cannot get storage controller"));
goto cleanup;
}
if (!storageController)
continue;
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.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 slot"));
goto cleanup;
}
rc = gVBoxAPI.UIMediumAttachment.GetMedium(imediumattach, &disk);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Cannot get medium"));
goto cleanup;
}
/* skip empty removable disk */
if (!disk) {
/* removable disks with empty (ejected) media won't be displayed
* in XML, but we need to update "sdCount" so that device names match
* in domain dumpxml and snapshot dumpxml
*/
if (storageBus == StorageBus_SATA || storageBus == StorageBus_SCSI ||
storageBus == StorageBus_SAS)
sdCount++;
VBOX_RELEASE(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.UIMedium.GetReadOnly(disk, &readOnly);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Cannot get read only attribute"));
goto cleanup;
}
def->dom->disks[diskCount]->dst = vboxGenerateMediumName(storageBus,
devicePort,
deviceSlot,
sdCount);
if (!def->dom->disks[diskCount]->dst) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Could not generate medium name for the disk "
"at: port:%d, slot:%d"), devicePort, deviceSlot);
ret = -1;
goto cleanup;
}
if (storageBus == StorageBus_IDE) {
def->dom->disks[diskCount]->bus = VIR_DOMAIN_DISK_BUS_IDE;
} else if (storageBus == StorageBus_SATA) {
sdCount++;
def->dom->disks[diskCount]->bus = VIR_DOMAIN_DISK_BUS_SATA;
} else if (storageBus == StorageBus_SCSI ||
storageBus == StorageBus_SAS) {
sdCount++;
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;
if (readOnly == PR_TRUE)
def->dom->disks[diskCount]->src->readonly = true;
def->dom->disks[diskCount]->src->type = VIR_STORAGE_TYPE_FILE;
diskCount++;
}
ret = 0;
cleanup:
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;
vboxDriverPtr data = dom->conn->privateData;
vboxIID 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];
char *ret = NULL;
if (!data->vboxObj)
return ret;
virCheckFlags(0, NULL);
if (openSessionForMachine(data, dom->uuid, &domiid, &machine) < 0)
goto cleanup;
if (!(snap = vboxDomainSnapshotGet(data, dom, machine, snapshot->name)))
goto cleanup;
if (VIR_ALLOC(def) < 0 || !(def->dom = virDomainDefNew()))
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
*/
virDomainDefSetMemoryTotal(def->dom, memorySize * 1024);
def->dom->os.type = VIR_DOMAIN_OSTYPE_HVM;
def->dom->os.arch = virArchFromHost();
gVBoxAPI.UIMachine.GetCPUCount(machine, &CPUCount);
if (virDomainDefSetVcpusMax(def->dom, CPUCount, data->xmlopt) < 0)
goto cleanup;
if (virDomainDefSetVcpus(def->dom, CPUCount) < 0)
goto cleanup;
if (vboxSnapshotGetReadWriteDisks(def, snapshot) < 0)
VIR_DEBUG("Could not get read write disks for snapshot");
if (vboxSnapshotGetReadOnlyDisks(def, snapshot) < 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, ×tamp);
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, data->caps, data->xmlopt,
virDomainDefFormatConvertXMLFlags(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)
{
vboxDriverPtr data = dom->conn->privateData;
vboxIID iid;
IMachine *machine = NULL;
nsresult rc;
PRUint32 snapshotCount;
int ret = -1;
if (!data->vboxObj)
return ret;
virCheckFlags(VIR_DOMAIN_SNAPSHOT_LIST_ROOTS |
VIR_DOMAIN_SNAPSHOT_LIST_METADATA, -1);
if (openSessionForMachine(data, dom->uuid, &iid, &machine) < 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)
{
vboxDriverPtr data = dom->conn->privateData;
vboxIID iid;
IMachine *machine = NULL;
nsresult rc;
ISnapshot **snapshots = NULL;
ssize_t i, count = 0;
int ret = -1;
if (!data->vboxObj)
return ret;
virCheckFlags(VIR_DOMAIN_SNAPSHOT_LIST_ROOTS |
VIR_DOMAIN_SNAPSHOT_LIST_METADATA, -1);
if (openSessionForMachine(data, dom->uuid, &iid, &machine) < 0)
goto cleanup;
if (flags & VIR_DOMAIN_SNAPSHOT_LIST_METADATA) {
ret = 0;
goto cleanup;
}
if (flags & VIR_DOMAIN_SNAPSHOT_LIST_ROOTS) {
vboxIID 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:
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)
{
vboxDriverPtr data = dom->conn->privateData;
vboxIID iid;
IMachine *machine = NULL;
ISnapshot *snapshot = NULL;
virDomainSnapshotPtr ret = NULL;
if (!data->vboxObj)
return ret;
virCheckFlags(0, NULL);
if (openSessionForMachine(data, dom->uuid, &iid, &machine) < 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)
{
vboxDriverPtr data = dom->conn->privateData;
vboxIID iid;
IMachine *machine = NULL;
ISnapshot *snapshot = NULL;
nsresult rc;
int ret = -1;
if (!data->vboxObj)
return ret;
virCheckFlags(0, -1);
if (openSessionForMachine(data, dom->uuid, &iid, &machine) < 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;
vboxDriverPtr data = dom->conn->privateData;
vboxIID iid;
IMachine *machine = NULL;
ISnapshot *snap = NULL;
ISnapshot *parent = NULL;
PRUnichar *nameUtf16 = NULL;
char *name = NULL;
nsresult rc;
virDomainSnapshotPtr ret = NULL;
if (!data->vboxObj)
return ret;
virCheckFlags(0, NULL);
if (openSessionForMachine(data, dom->uuid, &iid, &machine) < 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)
{
vboxDriverPtr data = dom->conn->privateData;
vboxIID iid;
IMachine *machine = NULL;
ISnapshot *snapshot = NULL;
PRUnichar *nameUtf16 = NULL;
char *name = NULL;
nsresult rc;
virDomainSnapshotPtr ret = NULL;
if (!data->vboxObj)
return ret;
virCheckFlags(0, NULL);
if (openSessionForMachine(data, dom->uuid, &iid, &machine) < 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;
vboxDriverPtr data = dom->conn->privateData;
vboxIID iid;
IMachine *machine = NULL;
ISnapshot *snap = NULL;
ISnapshot *current = NULL;
PRUnichar *nameUtf16 = NULL;
char *name = NULL;
nsresult rc;
int ret = -1;
if (!data->vboxObj)
return ret;
virCheckFlags(0, -1);
if (openSessionForMachine(data, dom->uuid, &iid, &machine) < 0)
goto cleanup;
if (!(snap = vboxDomainSnapshotGet(data, dom, machine, snapshot->name)))
goto cleanup;
rc = gVBoxAPI.UIMachine.GetCurrentSnapshot(machine, ¤t);
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;
vboxDriverPtr data = dom->conn->privateData;
vboxIID iid;
IMachine *machine = NULL;
ISnapshot *snap = NULL;
int ret = -1;
if (!data->vboxObj)
return ret;
virCheckFlags(0, -1);
if (openSessionForMachine(data, dom->uuid, &iid, &machine) < 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;
vboxDriverPtr data = dom->conn->privateData;
vboxIID domiid;
IMachine *machine = NULL;
ISnapshot *newSnapshot = NULL;
ISnapshot *prevSnapshot = NULL;
PRBool online = PR_FALSE;
PRUint32 state;
nsresult rc;
int ret = -1;
if (!data->vboxObj)
return ret;
virCheckFlags(0, -1);
if (openSessionForMachine(data, dom->uuid, &domiid, &machine) < 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(vboxDriverPtr data,
IConsole *console,
ISnapshot *snapshot)
{
IProgress *progress = NULL;
vboxIID 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(vboxDriverPtr 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;
vboxDriverPtr data = dom->conn->privateData;
virDomainSnapshotDefPtr def = NULL;
char *defXml = NULL;
vboxIID domiid;
nsresult rc;
IMachine *machine = NULL;
PRUnichar *settingsFilePathUtf16 = NULL;
char *settingsFilepath = NULL;
virVBoxSnapshotConfMachinePtr snapshotMachineDesc = NULL;
int isCurrent = -1;
char **searchResultTab = NULL;
ssize_t resultSize = 0;
int it = 0;
PRUnichar *machineNameUtf16 = NULL;
char *machineName = NULL;
char *nameTmpUse = NULL;
char *machineLocationPath = NULL;
PRUint32 aMediaSize = 0;
IMedium **aMedia = NULL;
int ret = -1;
if (!data->vboxObj)
return ret;
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,
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) < 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 *tmp = NULL;
vboxIID 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.CreateHardDisk(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);
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 *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);
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);
virStringListFree(searchResultTab);
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;
vboxDriverPtr data = dom->conn->privateData;
vboxIID domiid;
IMachine *machine = NULL;
ISnapshot *snap = NULL;
IConsole *console = NULL;
PRUint32 state;
nsresult rc;
vboxArray snapChildren = VBOX_ARRAY_INITIALIZER;
int ret = -1;
if (!data->vboxObj)
return ret;
virCheckFlags(VIR_DOMAIN_SNAPSHOT_DELETE_CHILDREN |
VIR_DOMAIN_SNAPSHOT_DELETE_METADATA_ONLY, -1);
if (openSessionForMachine(data, dom->uuid, &domiid, &machine) < 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)
{
vboxDriverPtr data = dom->conn->privateData;
IConsole *console = NULL;
vboxIID iid;
IMachine *machine = NULL;
nsresult rc;
char *tmp;
char *cacheDir;
int tmp_fd = -1;
unsigned int max_screen;
bool privileged = geteuid() == 0;
char *ret = NULL;
if (!data->vboxObj)
return ret;
virCheckFlags(0, NULL);
if (openSessionForMachine(data, dom->uuid, &iid, &machine) < 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 ((privileged && virAsprintf(&cacheDir, "%s/cache/libvirt", LOCALSTATEDIR) < 0) ||
(!privileged && !(cacheDir = virGetUserCacheDirectory()))) {
VBOX_RELEASE(machine);
return NULL;
}
if (virAsprintf(&tmp, "%s/vbox.screendump.XXXXXX", cacheDir) < 0) {
VBOX_RELEASE(machine);
VIR_FREE(cacheDir);
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);
VIR_FREE(cacheDir);
VBOX_RELEASE(machine);
vboxIIDUnalloc(&iid);
return ret;
}
#define MATCH(FLAG) (flags & (FLAG))
static int
vboxConnectListAllDomains(virConnectPtr conn,
virDomainPtr **domains,
unsigned int flags)
{
vboxDriverPtr data = conn->privateData;
vboxArray machines = VBOX_ARRAY_INITIALIZER;
char *machineNameUtf8 = NULL;
PRUnichar *machineNameUtf16 = NULL;
unsigned char uuid[VIR_UUID_BUFLEN];
vboxIID iid;
PRUint32 state;
nsresult rc;
size_t i;
virDomainPtr dom;
virDomainPtr *doms = NULL;
int count = 0;
bool active;
PRUint32 snapshotCount;
int ret = -1;
if (!data->vboxObj)
return ret;
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
* - persistence: 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];
int id = -1;
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);
if (active)
id = i + 1;
dom = virGetDomain(conn, machineNameUtf8, uuid, id);
VBOX_UTF8_FREE(machineNameUtf8);
VBOX_UTF16_FREE(machineNameUtf16);
if (!dom)
goto cleanup;
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++)
virObjectUnref(doms[i]);
}
VIR_FREE(doms);
gVBoxAPI.UArray.vboxArrayRelease(&machines);
return ret;
}
#undef MATCH
static int
vboxNodeGetInfo(virConnectPtr conn ATTRIBUTE_UNUSED,
virNodeInfoPtr nodeinfo)
{
return virCapabilitiesGetNodeInfo(nodeinfo);
}
static int
vboxNodeGetCellsFreeMemory(virConnectPtr conn ATTRIBUTE_UNUSED,
unsigned long long *freeMems,
int startCell,
int maxCells)
{
return virHostMemGetCellsFree(freeMems, startCell, maxCells);
}
static unsigned long long
vboxNodeGetFreeMemory(virConnectPtr conn ATTRIBUTE_UNUSED)
{
unsigned long long freeMem;
if (virHostMemGetInfo(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 virHostMemGetFreePages(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 virHostMemAllocPages(npages, pageSizes, pageCounts,
startCell, cellCount, add);
}
static int
vboxDomainHasManagedSaveImage(virDomainPtr dom, unsigned int flags)
{
vboxDriverPtr data = dom->conn->privateData;
vboxArray machines = VBOX_ARRAY_INITIALIZER;
vboxIID iid;
char *machineNameUtf8 = NULL;
PRUnichar *machineNameUtf16 = NULL;
unsigned char uuid[VIR_UUID_BUFLEN];
size_t i;
bool matched = false;
nsresult rc;
int ret = -1;
virCheckFlags(0, -1);
if (!data->vboxObj)
return ret;
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);
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
vboxDomainSendKey(virDomainPtr dom,
unsigned int codeset,
unsigned int holdtime,
unsigned int *keycodes,
int nkeycodes,
unsigned int flags)
{
int ret = -1;
vboxDriverPtr data = dom->conn->privateData;
IConsole *console = NULL;
vboxIID iid;
IMachine *machine = NULL;
IKeyboard *keyboard = NULL;
PRInt32 *keyDownCodes = NULL;
PRInt32 *keyUpCodes = NULL;
PRUint32 codesStored = 0;
nsresult rc;
size_t i;
int keycode;
if (!data->vboxObj)
return ret;
virCheckFlags(0, -1);
keyDownCodes = (PRInt32 *) keycodes;
if (VIR_ALLOC_N(keyUpCodes, nkeycodes) < 0)
return ret;
/* translate keycodes to xt and generate keyup scancodes */
for (i = 0; i < nkeycodes; i++) {
if (codeset != VIR_KEYCODE_SET_XT) {
keycode = virKeycodeValueTranslate(codeset, VIR_KEYCODE_SET_XT,
keyDownCodes[i]);
if (keycode < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("cannot translate keycode %u of %s codeset to"
" xt keycode"),
keyDownCodes[i],
virKeycodeSetTypeToString(codeset));
goto cleanup;
}
keyDownCodes[i] = keycode;
}
keyUpCodes[i] = keyDownCodes[i] + 0x80;
}
if (openSessionForMachine(data, dom->uuid, &iid, &machine) < 0)
goto cleanup;
rc = gVBoxAPI.UISession.OpenExisting(data, &iid, machine);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_OPERATION_FAILED,
_("Unable to open VirtualBox session with domain %s"),
dom->name);
goto cleanup;
}
rc = gVBoxAPI.UISession.GetConsole(data->vboxSession, &console);
if (NS_FAILED(rc) || !console) {
virReportError(VIR_ERR_OPERATION_FAILED,
_("Unable to get Console object for domain %s"),
dom->name);
goto cleanup;
}
rc = gVBoxAPI.UIConsole.GetKeyboard(console, &keyboard);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_OPERATION_FAILED,
_("Unable to get Keyboard object for domain %s"),
dom->name);
goto cleanup;
}
rc = gVBoxAPI.UIKeyboard.PutScancodes(keyboard, nkeycodes, keyDownCodes,
&codesStored);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_OPERATION_FAILED,
_("Unable to send keyboard scancodes for domain %s"),
dom->name);
goto cleanup;
}
/* since VBOX does not support holdtime, simulate it by sleeping and
then sending the release key scancodes */
if (holdtime > 0)
usleep(holdtime * 1000);
rc = gVBoxAPI.UIKeyboard.PutScancodes(keyboard, nkeycodes, keyUpCodes,
&codesStored);
if (NS_FAILED(rc)) {
virReportError(VIR_ERR_OPERATION_FAILED,
_("Unable to send keyboard scan codes to domain %s"),
dom->name);
goto cleanup;
}
ret = 0;
cleanup:
VIR_FREE(keyUpCodes);
VBOX_RELEASE(keyboard);
VBOX_RELEASE(console);
gVBoxAPI.UISession.Close(data->vboxSession);
VBOX_RELEASE(machine);
vboxIIDUnalloc(&iid);
return ret;
}
/**
* Function Tables
*/
static virHypervisorDriver vboxCommonDriver = {
.name = "VBOX",
.connectURIProbe = vboxConnectURIProbe,
.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 */
.domainDefineXMLFlags = vboxDomainDefineXMLFlags, /* 1.2.12 */
.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 */
.domainHasManagedSaveImage = vboxDomainHasManagedSaveImage, /* 1.2.13 */
.domainSendKey = vboxDomainSendKey, /* 1.2.15 */
.domainScreenshot = vboxDomainScreenshot, /* 0.9.2 */
};
virHypervisorDriverPtr vboxGetHypervisorDriver(uint32_t uVersion)
{
/* Install gVBoxAPI according to the vbox API version. */
int result = 0;
installUniformedAPI(gVBoxAPI, result);
if (result < 0) {
VIR_WARN("Libvirt doesn't support VirtualBox API version %u",
uVersion);
return NULL;
}
return &vboxCommonDriver;
}