/* * 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 "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 "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_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; 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; } 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; } 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); } 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; } int vboxConnectClose(virConnectPtr conn) { vboxGlobalData *data = conn->privateData; VIR_DEBUG("%s: in vboxClose", conn->driver->name); vboxUninitialize(data); conn->privateData = NULL; return 0; } 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); } 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; } char *vboxConnectGetHostname(virConnectPtr conn ATTRIBUTE_UNUSED) { return virGetHostname(); } int vboxConnectIsSecure(virConnectPtr conn ATTRIBUTE_UNUSED) { /* Driver is using local, non-network based transport */ return 1; } int vboxConnectIsEncrypted(virConnectPtr conn ATTRIBUTE_UNUSED) { /* No encryption is needed, or used on the local transport*/ return 0; } int vboxConnectIsAlive(virConnectPtr conn ATTRIBUTE_UNUSED) { return 1; } 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; } char *vboxConnectGetCapabilities(virConnectPtr conn) { VBOX_OBJECT_CHECK(conn, char *, NULL); vboxDriverLock(data); ret = virCapabilitiesFormatXML(data->caps); vboxDriverUnlock(data); return ret; } 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; } 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; } 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; } 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); VIR_DEBUG("def->os.loader %s", def->os.loader); 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); } } 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); } 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 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; } 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; } int vboxDomainCreate(virDomainPtr dom) { return vboxDomainCreateWithFlags(dom, 0); } 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; } 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; } 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; } 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; } 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; } 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; } 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; } int vboxDomainShutdown(virDomainPtr dom) { return vboxDomainShutdownFlags(dom, 0); } 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; } 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; } int vboxDomainDestroy(virDomainPtr dom) { return vboxDomainDestroyFlags(dom, 0); } 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; } 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; }