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