HostImpl.cpp revision bc653c2679122d7ae19b49f1fcf497b9edd7ea70
1N/A * VirtualBox COM class implementation 1N/A * Copyright (C) 2006-2007 innotek GmbH 1N/A * This file is part of VirtualBox Open Source Edition (OSE), as 1N/A * you can redistribute it and/or modify it under the terms of the GNU 1N/A * General Public License (GPL) as published by the Free Software 1N/A * Foundation, in version 2 as it comes in the "COPYING" file of the 1N/A * VirtualBox OSE distribution. VirtualBox OSE is distributed in the 1N/A * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. 1N/A/* bird: This is a hack to work around conflicts between these linux kernel headers 1N/A * and the GLIBC tcpip headers. They have different declarations of the 4 1N/A * standard byte order functions. */ 1N/A// /* These are defined by libhal.h and by VBox header files. */ 1N/A#
endif /* RT_OS_LINUX */ 1N/A#
endif /* RT_OS_SOLARIS */ 1N/A#
endif /* RT_OS_WINDOWS */ 1N/A// constructor / destructor 1N/A///////////////////////////////////////////////////////////////////////////// 1N/A///////////////////////////////////////////////////////////////////////////// 1N/A * Initializes the host object. 1N/A * @returns COM result indicator 1N/A * @param parent handle of our parent object 1N/A /** @todo handle !mUSBProxySerivce->isActive() and mUSBProxyService->getLastError() 1N/A * and somehow report or whatever that the proxy failed to startup. 1N/A * Also, there might be init order issues... */ 1N/A * Uninitializes the host object and sets the ready flag to FALSE. 1N/A * Called either from FinalRelease() or by the parent when it gets destroyed. 1N/A /* wait for USB proxy service to terminate before we uninit all USB 1N/A /* uninit all USB device filters still referenced by clients */ 1N/A///////////////////////////////////////////////////////////////////////////// 1N/A * Returns a list of host DVD drives. 1N/A * @returns COM status code 1N/A * @param drives address of result pointer 1N/A // Not all Solaris versions ship with libhal. 1N/A // So use a fallback approach similar to Linux. 1N/A // this might work on Solaris version older than Nevada. 1N/A // check the mounted drives 1N/A#
endif /* USE_LIBHAL defined */ 1N/A // On Linux without hal, the situation is much more complex. We will take a 1N/A // heuristical approach and also allow the user to specify a list of host 1N/A // CDROMs using an environment variable. 1N/A // The general strategy is to try some known device names and see of they 1N/A // exist. At last, we'll enumerate the /etc/fstab file (luckily there's an 1N/A // API to parse it) for CDROM devices. Ok, let's start! 1N/A // this is a good guess usually // check the mounted drives // check the drives that can be mounted * Returns a list of host floppy drives. * @returns COM status code * @param drives address of result pointer #
endif /* USE_LIBHAL defined */ // As with the CDROMs, on Linux we have to take a multi-level approach // involving parsing the mount tables. As this is not bulletproof, we'll // give the user the chance to override the detection by an environment // variable and skip the detection. // check if this is an acceptable device // we assume that a floppy is always /dev/fd[x] with x from 0 to 7 for (
int i = 0; i <=
7; i++)
* Returns a list of host network interfaces. * @returns COM status code * @param drives address of result pointer static const char *
NetworkKey =
"SYSTEM\\CurrentControlSet\\Control\\Network\\" "{4D36E972-E325-11CE-BFC1-08002BE10318}";
/* put a trailing zero, just in case (see MSDN) */ /* create a new object and add it to the list */ /* remove the curly bracket at the end */ #
endif /* RT_OS_WINDOWS */ /* Note: The GUI depends on this method returning E_NOTIMPL with no * extended error info to indicate that USB is simply not available * (w/o treting it as a failure), for example, as in OSE */ /* Note: The GUI depends on this method returning E_NOTIMPL with no * extended error info to indicate that USB is simply not available * (w/o treting it as a failure), for example, as in OSE */ * Returns the number of installed logical processors * @returns COM status code * @param count address of result variable * Returns the (approximate) speed of the host CPU in MHz * @returns COM status code * @param speed address of result variable /** @todo Add a runtime function for this which uses GIP. */ * Returns a description string for the host CPU * @returns COM status code * @param description address of result variable * Returns the amount of installed system memory in megabytes * @returns COM status code * @param size address of result variable * Returns the current system memory free space in megabytes * @returns COM status code * @param available address of result variable * Returns the name string of the host operating system * @returns COM status code * @param os address of result variable * Returns the version string of the host operating system * @returns COM status code * @param os address of result variable * Returns the current host time in milliseconds since 1970-01-01 UTC. * @returns COM status code * @param time address of result variable //////////////////////////////////////////////////////////////////////////////// * Returns TRUE if the Windows version is 6.0 or greater (i.e. it's Vista and * later OSes) and it has the UAC (User Account Control) feature enabled. LogFlowFunc ((
"dwMajorVersion=%d, dwMinorVersion=%d\n",
/* we are interested only in Vista (and newer versions...). In all * earlier versions UAC is not present. */ /* the default EnableLUA value is 1 (Enabled) */ "Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System",
/* for SVCHlpMsg::CreateHostNetworkInterface */ /* for SVCHlpMsg::RemoveHostNetworkInterface */ /* first check whether an interface with the given name already exists */ tr (
"Host network interface '%ls' already exists"),
aName);
/* create a progress object */ Bstr (
tr (
"Creating host network interface")),
FALSE /* aCancelable */);
/* create a new uninitialized host interface object */ /* create the networkInterfaceHelperClient() argument */ static_cast <
void *> (d.
get()),
/* d is now owned by networkInterfaceHelperClient(), so release it */ /* first check whether an interface with the given name already exists */ tr (
"Host network interface with UUID {%Vuuid} does not exist"),
/* return the object to be removed to the caller */ /* create a progress object */ Bstr (
tr (
"Removing host network interface")),
FALSE /* aCancelable */);
/* create the networkInterfaceHelperClient() argument */ static_cast <
void *> (d.
get()),
/* d is now owned by networkInterfaceHelperClient(), so release it */ #
endif /* RT_OS_WINDOWS */ /* Note: The GUI depends on this method returning E_NOTIMPL with no * extended error info to indicate that USB is simply not available * (w/o treting it as a failure), for example, as in OSE */ tr (
"The given USB device filter is not created within " "this VirtualBox instance"));
tr (
"The given USB device filter is already in the list"));
/* iterate to the position... */ /* notify the proxy (only when the filter is active) */ /* save the global settings */ /* Note: The GUI depends on this method returning E_NOTIMPL with no * extended error info to indicate that USB is simply not available * (w/o treting it as a failure), for example, as in OSE */ tr (
"The USB device filter list is empty"));
tr (
"Invalid position: %lu (must be in range [0, %lu])"),
/* iterate to the position... */ /* ...get an element from there... */ /* notify the proxy (only when the filter is active) */ /* save the global settings */ /* Note: The GUI depends on this method returning E_NOTIMPL with no * extended error info to indicate that USB is simply not available * (w/o treting it as a failure), for example, as in OSE */ // public methods only for internal purposes //////////////////////////////////////////////////////////////////////////////// * Called by setter methods of all USB device filters. // update the filter in the proxy // save the global settings... yeah, on every single filter property change /* error info is set by init() when appropriate */ /* notify the proxy (only when the filter is active) */ /* first, delete the entry */ /* action is mandatory */ * Requests the USB proxy service to capture the given host USB device. * When the request is completed, * IInternalSessionControl::onUSBDeviceAttach() will be called on the given * Called by Console from the VM process (throug IInternalMachineControl). * Must return extended error info in case of errors. tr (
"USB device with UUID {%Vuuid} is not currently attached to the host"),
tr (
"USB device '%s' with UUID {%Vuuid} is busy (waiting for a pending " "state change). Please try later"),
tr (
"USB device '%s' with UUID {%Vuuid} cannot be accessed by guest " tr (
"USB device '%s' with UUID {%Vuuid} is being exclusively used by the " /* Machine::name() requires a read lock */ tr (
"USB device '%s' with UUID {%Vuuid} is already captured by the virtual " /* try to capture the device */ * Notification from the VM process that it is going to detach (\a aDone = false) * or that is has just detach (\a aDone = true) the given USB device. * When \a aDone = false we only inform the USB Proxy about what the vm is * up to so it doesn't get confused and create a new USB host device object * When \a aDone = true we replay all filters against the given USB device * excluding filters of the machine the device is currently marked as * When the \a aDone = true request is completed, * IInternalSessionControl::onUSBDeviceDetach() will be called on the given * Called by Console from the VM process (throug IInternalMachineControl). LogFlowThisFunc ((
"id={%Vuuid} state=%d isStatePending=%RTbool pendingState=%d aDone=%RTbool\n",
tr (
"USB device '%s' with UUID {%Vuuid} is busy (waiting for a pending " "state change). Please try later"),
/* If an async detach operation is still pending (darwin), postpone the setHeld() + the re-applying of filters until it is completed. We indicate this by moving to the '*Filters' state variant. */ tr (
"USB device '%s' with UUID {%Vuuid} is busy (waiting for a pending " "state change). Please try later"),
/* re-apply filters on the device before giving it back to the host */ * Asks the USB proxy service to capture all currently available USB devices * that match filters of the given machine. * When the request is completed, * IInternalSessionControl::onUSBDeviceDetach() will be called on the given * machine object per every captured USB device. * Called by Console from the VM process (through IInternalMachineControl) * @note Locks this object for reading (@todo for writing now, until switched * to the new locking scheme). /* skip pending devices */ * Replays all filters against all USB devices currently marked as captured * by the given machine (excluding this machine's filters). * Called by Console from the VM process (throug IInternalMachineControl) * upon normal VM termination or by SessionMachine::uninit() upon abnormal * @note Locks this object for reading (@todo for writing now, until switched * to the new locking scheme). /* re-apply filters on the device before giving it back to the //////////////////////////////////////////////////////////////////////////////// * Helper function to query the hal subsystem for information about DVD drives attached to the * @returns true if information was successfully obtained, false otherwise * @retval list drives found will be attached to this list "storage.drive_type",
"cdrom",
/* Hal is installed and working, so if no devices are reported, assume /* The CD/DVD ioctls work only for raw device nodes. */ // if (validateDevice(devNode, true)) /* We do not check the error here, as this field may LogRel((
"Host::COMGETTER(DVDDrives): failed to get property \"info.product\" for device %s. dbus error: %s (%s)\n",
// LogRel(("Host::COMGETTER(DVDDrives): failed to validate the block device %s as a DVD drive\n")); LogRel((
"Host::COMGETTER(DVDDrives): failed to get property \"block.device\" for device %s. dbus error: %s (%s)\n",
LogRel((
"Host::COMGETTER(DVDDrives): failed to set libhal connection to dbus.\n"));
LogRel((
"Host::COMGETTER(DVDDrives): failed to get a libhal context - out of memory?\n"));
* Helper function to query the hal subsystem for information about floppy drives attached to the * @returns true if information was successfully obtained, false otherwise * @retval list drives found will be attached to this list "storage.drive_type",
"floppy",
/* Hal is installed and working, so if no devices are reported, assume /* An error occurred. The attribute "storage.drive_type" probably didn't exist. */ // if (validateDevice(devNode, false)) /* We do not check the error here, as this field may LogRel((
"Host::COMGETTER(FloppyDrives): failed to get property \"info.product\" for device %s. dbus error: %s (%s)\n",
// LogRel(("Host::COMGETTER(FloppyDrives): failed to validate the block device %s as a floppy drive\n")); LogRel((
"Host::COMGETTER(FloppyDrives): failed to get property \"block.device\" for device %s. dbus error: %s (%s)\n",
LogRel((
"Host::COMGETTER(FloppyDrives): failed to set libhal connection to dbus.\n"));
LogRel((
"Host::COMGETTER(FloppyDrives): failed to get a libhal context - out of memory?\n"));
#
endif /* VBOX_USE_HAL defined */ * Helper function to parse the given mount file and add found entries // use strstr here to cover things fs types like "udf,iso9660" /** @todo check whether we've already got the drive in our list! */ // skip devices we are not interested in if ((*
mountName &&
mountName[0] ==
'/') &&
// skip 'fake' devices (like -hosts, proc, fd, swap) * Helper function to check whether the given device node is a valid drive // first a simple stat() call // now try to open the device // this call will finally reveal the whole truth /// @todo do some more testing, maybe a nice IOCTL! #
endif // RT_OS_LINUX || RT_OS_SOLARIS * Applies all (golbal and VM) filters to the given USB device. The device * must be either a newly attached device or a device released by a VM. * This method will request the USB proxy service to release the device (give * it back to the host) if none of the global or VM filters want to capture * @param aDevice USB device to apply filters to. * @param aMachine Machine the device was released by or @c NULL. * @note the method must be called from under this object's write lock and * from the aDevice's write lock. /// @todo must check for read lock, it's enough here /* ignore unsupported devices */ /* ignore unavailable devices as well */ /// @todo it may be better to take a copy of filters to iterate and leave /// the host lock before calling HostUSBDevice:requestCapture() (which /// calls the VM process). /* apply global filters */ /* request to give the device back to the host*/ /* nothing to do any more */ /* apply machine filters */ /* skip the machine the device was just detached from */ /* no matched machine filters, check what to do */ /* no any filter matched at all */ /* request to give the device back to the host */ /* there was a global Hold filter */ * Runs through filters of the given machine and asks the USB proxy service * to capture the given USB device when there is a match. * @param aMachine Machine whose filters are to be run. * @param aDevice USB device, a candidate for auto-capturing. * @return @c true if there was a match and @c false otherwise. * @note the method must be called from under this object's write lock and * from the aDevice's write lock. * @note Locks aMachine for reading. /// @todo must check for read lock, it's enough here /* try to capture the device */ * Called by USB proxy service when a new device is physically attached * @param aDevice Pointer to the device which has been attached. LogFlowThisFunc ((
"id={%Vuuid} state=%d isStatePending=%RTbool pendingState=%d\n",
/* add to the collecion */ * Called by USB proxy service when the device is physically detached * @param aDevice Pointer to the device which has been detached. LogFlowThisFunc ((
"id={%Vuuid} state=%d isStatePending=%RTbool pendingState=%d\n",
/* remove from the collecion */ /* Detach the device from any machine currently using it, reset all data and uninitialize the device object. */ * Called by USB proxy service when the state of the device has changed * either because of the state change request or because of some external * @param aDevice The device in question. LogFlowThisFunc ((
"id={%Vuuid} state=%d isStatePending=%RTbool pendingState=%d\n",
/* it was a state change request */ /* The device has completed an asynchronous detach operation, subject it to the filters and such if the current state permits this. (handlePendingStateChange will disassociate itself from the machine.) */ Log ((
"USB: running filters on async detached device\n"));
Log ((
"USB: async detached devices reappeared in stated %d instead of %d!\n",
/* The device has gone from being unavailable (not subject to filters) to being available / busy. This transition can be triggered by udevd or manual permission changes on Linux. On all systems may be triggered by the host ceasing to use the device - like unmounting an MSD in the Finder or invoking the "Safely remove XXXX" stuff on Windows (perhaps). */ /* some external state change */ /// @todo re-run all USB filters probably * Checks for the presense and status of the USB Proxy Service. * Returns S_OK when the Proxy is present and OK, or E_FAIL and a * corresponding error message otherwise. Intended to be used by methods * that rely on the Proxy Service availability. * @note This method may return a warning result code. It is recommended to use * MultiError to store the return value. * @note Locks this object for reading. /* disable the USB controller completely to avoid assertions if the * USB proxy service could not start. */ tr (
"Could not load the Host USB Proxy Service (%Vrc). " "The service might be not installed on the host computer"),
tr (
"The USB Proxy Service has not yet been ported to this host"));
tr (
"Could not load the Host USB Proxy service (%Vrc)"),
/* The original source of the VBoxTAP adapter creation/destruction code has the following copyright */ Copyright 2004 by the Massachusetts Institute of Technology Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation, and that the name of the Massachusetts Institute of Technology (M.I.T.) not be used in advertising or publicity pertaining to distribution of the software without specific, written M.I.T. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL M.I.T. BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * Use the IShellFolder API to rename the connection. /* This is the GUID for the network connections folder. It is constant. * {7007ACC7-3202-11D1-AAD2-00805FC1270E} */ 0x7007ACC7,
0x3202,
0x11D1, {
0xAA,
0xD2,
0x00,
0x80,
0x5F,
0xC1,
0x27,
0x0E /* Build the display name in the form "::{GUID}". */ /* Create an instance of the network connections folder. */ /* Parse the display name. */ /* First try the IShellFolder interface, which was unimplemented * for the network connections folder before XP. */ /** @todo that code doesn't seem to work! */ /* The IShellFolder interface is not implemented on this platform. * Try the (undocumented) HrRenameConnection API in the netshell /* for our purposes, 2k buffer is more * than enough to obtain the hardware ID * of the VBoxTAP driver. */ /* initialize the structure size */ /* copy the net class GUID */ /* create an empty device info set associated with the net class GUID */ SetErrBreak ((
"SetupDiCreateDeviceInfoList failed (0x%08X)",
/* get the class name from GUID */ SetErrBreak ((
"SetupDiClassNameFromGuid failed (0x%08X)",
/* create a device info element and add the new device instance SetErrBreak ((
"SetupDiCreateDeviceInfo failed (0x%08X)",
/* select the newly created device info to be the currently SetErrBreak ((
"SetupDiSetSelectedDevice failed (0x%08X)",
/* build a list of class drivers */ SetErrBreak ((
"SetupDiBuildDriverInfoList failed (0x%08X)",
/* enumerate the driver info list */ /* if the function failed and GetLastError() returned * ERROR_NO_MORE_ITEMS, then we have reached the end of the * list. Othewise there was something wrong with this /* if we successfully find the hardware ID and it turns out to * be the one for the loopback driver, then we are done. */ /* pDriverInfoDetail->HardwareID is a MULTISZ string. Go through the * whole list and see if there is a match somewhere. */ SetErrBreak ((
tr (
"Could not find Host Interface Networking driver! " /* set the loopback driver to be the currently selected */ SetErrBreak ((
"SetupDiSetSelectedDriver failed (0x%08X)",
/* register the phantom device to prepare for install */ SetErrBreak ((
"SetupDiCallClassInstaller failed (0x%08X)",
/* registered, but remove if errors occur in the following code */ /* ask the installer if we can install the device */ SetErrBreak ((
"SetupDiCallClassInstaller (DIF_ALLOW_INSTALL) failed (0x%08X)",
/* install the files first */ SetErrBreak ((
"SetupDiCallClassInstaller (DIF_INSTALLDEVICEFILES) failed (0x%08X)",
/* get the device install parameters and disable filecopy */ SetErrBreak ((
"SetupDiSetDeviceInstallParams failed (0x%08X)",
* Register any device-specific co-installers for this device, SetErrBreak ((
"SetupDiCallClassInstaller (DIF_REGISTER_COINSTALLERS) failed (0x%08X)",
* install any installer-specified interfaces. * and then do the real install SetErrBreak ((
"SetupDiCallClassInstaller (DIF_INSTALLINTERFACES) failed (0x%08X)",
SetErrBreak ((
"SetupDiCallClassInstaller (DIF_INSTALLDEVICE) failed (0x%08X)",
/* Figure out NetCfgInstanceId */ SetErrBreak ((
"Failed to set interface name (ret=0x%08X, " "pCfgGuidString='%ls', cbSize=%d)",
/* an error has occured, but the device is registered, we must remove it */ /* destroy the driver info list */ /* clean up the device info set */ /* return the network connection GUID on success */ /* remove the curly bracket at the end */ /* We have to find the device instance ID through a registry search */ "SYSTEM\\CurrentControlSet\\Control\\Network\\" "{4D36E972-E325-11CE-BFC1-08002BE10318}\\{%s}",
tr (
"Host interface network is not found in registry (%s) [1]"),
tr (
"Host interface network is not found in registry (%s) [2]"),
tr (
"Host interface network is not found in registry (%s) [3]"),
* Now we are going to enumerate all network devices and * wait until we encounter the right device instance ID /* initialize the structure size */ /* copy the net class GUID */ /* return a device info set contains all installed devices of the Net class */ /* enumerate the driver info list */ /* try to get the hardware ID registry property */ /* something is wrong. This shouldn't have worked with a NULL buffer */ /* get the device instance ID */ /* compare to what we determined before */ SetErrBreak ((
tr (
"Host Interface Network driver not found (0x%08X)"),
SetErrBreak ((
"SetupDiSetSelectedDevice failed (0x%08X)",
SetErrBreak ((
"SetupDiCallClassInstaller (DIF_REMOVE) failed (0x%08X)",
/* clean up the device info set */ LogFlowFunc ((
"aClient={%p}, aProgress={%p}, aUser={%p}\n",
/* "cleanup only" mode, just return (it will free aUser) */ /* write message and parameters */ /* initialize the object returned to the caller by * CreateHostNetworkInterface() */ /* read the error message */ "Invalid message code %d (%08lX)\n",
/* write message and parameters */ /* read the error message */ "Invalid message code %d (%08lX)\n",
"Invalid message code %d (%08lX)\n",
/* write success followed by GUID */ /* write failure followed by error message */ /* write parameter-less success */ /* write failure followed by error message */ #
endif /* RT_OS_WINDOWS */