VirtualBox

source: vbox/trunk/src/VBox/Main/HostImpl.cpp@ 1158

Last change on this file since 1158 was 1139, checked in by vboxsync, 18 years ago

Fixed a typo.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 80.8 KB
Line 
1/** @file
2 * VirtualBox COM class implementation
3 */
4
5/*
6 * Copyright (C) 2006 InnoTek Systemberatung GmbH
7 *
8 * This file is part of VirtualBox Open Source Edition (OSE), as
9 * available from http://www.virtualbox.org. This file is free software;
10 * you can redistribute it and/or modify it under the terms of the GNU
11 * General Public License as published by the Free Software Foundation,
12 * in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
13 * distribution. VirtualBox OSE is distributed in the hope that it will
14 * be useful, but WITHOUT ANY WARRANTY of any kind.
15 *
16 * If you received this file as part of a commercial VirtualBox
17 * distribution, then only the terms of your commercial VirtualBox
18 * license agreement apply instead of the previous paragraph.
19 */
20
21#ifdef __LINUX__
22#include <sys/types.h>
23#include <sys/stat.h>
24#include <unistd.h>
25#include <sys/ioctl.h>
26#include <fcntl.h>
27#include <mntent.h>
28/* bird: This is a hack to work around conflicts between these linux kernel headers
29 * and the GLIBC tcpip headers. They have different declarations of the 4
30 * standard byte order functions. */
31#define _LINUX_BYTEORDER_GENERIC_H
32#include <linux/cdrom.h>
33#include <errno.h>
34#endif /* __LINUX __ */
35
36#ifdef __WIN__
37#define _WIN32_DCOM
38#include <windows.h>
39#include <shellapi.h>
40#define INITGUID
41#include <guiddef.h>
42#include <devguid.h>
43#include <objbase.h>
44#include <setupapi.h>
45#include <shlobj.h>
46#include <cfgmgr32.h>
47#endif /* __WIN__ */
48
49
50
51#include "HostImpl.h"
52#include "HostDVDDriveImpl.h"
53#include "HostFloppyDriveImpl.h"
54#include "HostUSBDeviceImpl.h"
55#include "USBControllerImpl.h"
56#include "USBDeviceFilterImpl.h"
57#include "USBProxyService.h"
58#include "VirtualBoxImpl.h"
59#include "MachineImpl.h"
60#include "Logging.h"
61
62#ifdef __WIN__
63#include "HostNetworkInterfaceImpl.h"
64#endif
65
66#include <VBox/usb.h>
67#include <VBox/err.h>
68#include <iprt/string.h>
69#include <iprt/system.h>
70#include <iprt/time.h>
71#include <iprt/param.h>
72
73#include <stdio.h>
74
75#include <algorithm>
76
77// constructor / destructor
78/////////////////////////////////////////////////////////////////////////////
79
80HRESULT Host::FinalConstruct()
81{
82 return S_OK;
83}
84
85void Host::FinalRelease()
86{
87 if (isReady())
88 uninit();
89}
90
91// public initializer/uninitializer for internal purposes only
92/////////////////////////////////////////////////////////////////////////////
93
94/**
95 * Initializes the host object.
96 *
97 * @returns COM result indicator
98 * @param parent handle of our parent object
99 */
100HRESULT Host::init (VirtualBox *parent)
101{
102 LogFlowMember(("Host::init(): isReady=%d\n", isReady()));
103
104 ComAssertRet (parent, E_INVALIDARG);
105
106 AutoLock lock(this);
107 ComAssertRet (!isReady(), E_UNEXPECTED);
108
109 mParent = parent;
110
111#if defined (__LINUX__) && defined (VBOX_WITH_USB)
112 mUSBProxyService = new USBProxyServiceLinux (this);
113#elif defined (__WIN__) && defined (VBOX_WITH_USB)
114 mUSBProxyService = new USBProxyServiceWin32 (this);
115#else
116 mUSBProxyService = new USBProxyService (this);
117#endif
118 /** @todo handle !mUSBProxySerivce->isActive() and mUSBProxyService->getLastError()
119 * and somehow report or whatever that the proxy failed to startup.
120 * Also, there might be init order issues... */
121
122 setReady(true);
123 return S_OK;
124}
125
126/**
127 * Uninitializes the host object and sets the ready flag to FALSE.
128 * Called either from FinalRelease() or by the parent when it gets destroyed.
129 */
130void Host::uninit()
131{
132 LogFlowMember(("Host::uninit(): isReady=%d\n", isReady()));
133
134 AssertReturn (isReady(), (void) 0);
135
136 // uninit all USB device filters still referenced by clients
137 uninitDependentChildren();
138
139 delete mUSBProxyService;
140 mUSBProxyService = NULL;
141
142 mUSBDeviceFilters.clear();
143 mUSBDevices.clear();
144
145 setReady (FALSE);
146}
147
148// IHost properties
149/////////////////////////////////////////////////////////////////////////////
150
151/**
152 * Returns a list of host DVD drives.
153 *
154 * @returns COM status code
155 * @param drives address of result pointer
156 */
157STDMETHODIMP Host::COMGETTER(DVDDrives) (IHostDVDDriveCollection **drives)
158{
159 if (!drives)
160 return E_POINTER;
161 AutoLock lock(this);
162 CHECK_READY();
163 std::list <ComObjPtr <HostDVDDrive> > list;
164
165#if defined(__WIN__)
166 int sz = GetLogicalDriveStrings(0, NULL);
167 TCHAR *hostDrives = new TCHAR[sz+1];
168 GetLogicalDriveStrings(sz, hostDrives);
169 wchar_t driveName[3] = { '?', ':', '\0' };
170 TCHAR *p = hostDrives;
171 do
172 {
173 if (GetDriveType(p) == DRIVE_CDROM)
174 {
175 driveName[0] = *p;
176 ComObjPtr <HostDVDDrive> hostDVDDriveObj;
177 hostDVDDriveObj.createObject();
178 hostDVDDriveObj->init (Bstr (driveName));
179 list.push_back (hostDVDDriveObj);
180 }
181 p += _tcslen(p) + 1;
182 }
183 while (*p);
184 delete[] hostDrives;
185#elif defined(__LINUX__)
186 // On Linux, the situation is much more complex. There is no simple API so
187 // we will take a more creative approach. As there is only a heuristical
188 // approach on Linux, we'll allow the user to specify a list of host CDROMs
189 // using and an environment variable.
190 // The general strategy is to try some known device names and see of they
191 // exist. At last, we'll enumerate the /etc/fstab file (luckily there's an
192 // API to parse it) for CDROM devices. Ok, let's start!
193
194 if (getenv("VBOX_CDROM"))
195 {
196 char *cdromEnv = strdupa(getenv("VBOX_CDROM"));
197 char *cdromDrive;
198 cdromDrive = strtok(cdromEnv, ":");
199 while (cdromDrive)
200 {
201 if (validateDevice(cdromDrive, true))
202 {
203 ComObjPtr <HostDVDDrive> hostDVDDriveObj;
204 hostDVDDriveObj.createObject();
205 hostDVDDriveObj->init (Bstr (cdromDrive));
206 list.push_back (hostDVDDriveObj);
207 }
208 cdromDrive = strtok(NULL, ":");
209 }
210 }
211 else
212 {
213 // this is a good guess usually
214 if (validateDevice("/dev/cdrom", true))
215 {
216 ComObjPtr <HostDVDDrive> hostDVDDriveObj;
217 hostDVDDriveObj.createObject();
218 hostDVDDriveObj->init (Bstr ("/dev/cdrom"));
219 list.push_back (hostDVDDriveObj);
220 }
221
222 // check the mounted drives
223 parseMountTable((char*)"/etc/mtab", list);
224
225 // check the drives that can be mounted
226 parseMountTable((char*)"/etc/fstab", list);
227 }
228#else
229 /* PORTME */
230#endif
231
232 ComObjPtr<HostDVDDriveCollection> collection;
233 collection.createObject();
234 collection->init (list);
235 collection.queryInterfaceTo(drives);
236 return S_OK;
237}
238
239/**
240 * Returns a list of host floppy drives.
241 *
242 * @returns COM status code
243 * @param drives address of result pointer
244 */
245STDMETHODIMP Host::COMGETTER(FloppyDrives) (IHostFloppyDriveCollection **drives)
246{
247 if (!drives)
248 return E_POINTER;
249 AutoLock lock(this);
250 CHECK_READY();
251
252 std::list <ComObjPtr <HostFloppyDrive> > list;
253
254#ifdef __WIN__
255 int sz = GetLogicalDriveStrings(0, NULL);
256 TCHAR *hostDrives = new TCHAR[sz+1];
257 GetLogicalDriveStrings(sz, hostDrives);
258 wchar_t driveName[3] = { '?', ':', '\0' };
259 TCHAR *p = hostDrives;
260 do
261 {
262 if (GetDriveType(p) == DRIVE_REMOVABLE)
263 {
264 driveName[0] = *p;
265 ComObjPtr <HostFloppyDrive> hostFloppyDriveObj;
266 hostFloppyDriveObj.createObject();
267 hostFloppyDriveObj->init (Bstr (driveName));
268 list.push_back (hostFloppyDriveObj);
269 }
270 p += _tcslen(p) + 1;
271 }
272 while (*p);
273 delete[] hostDrives;
274#elif defined(__LINUX__)
275 // As with the CDROMs, on Linux we have to take a multi-level approach
276 // involving parsing the mount tables. As this is not bulletproof, we'll
277 // give the user the chance to override the detection by an environment
278 // variable and skip the detection.
279
280 if (getenv("VBOX_FLOPPY"))
281 {
282 char *floppyEnv = getenv("VBOX_FLOPPY");
283 char *floppyDrive;
284 floppyDrive = strtok(floppyEnv, ":");
285 while (floppyDrive)
286 {
287 // check if this is an acceptable device
288 if (validateDevice(floppyDrive, false))
289 {
290 ComObjPtr <HostFloppyDrive> hostFloppyDriveObj;
291 hostFloppyDriveObj.createObject();
292 hostFloppyDriveObj->init (Bstr (floppyDrive));
293 list.push_back (hostFloppyDriveObj);
294 }
295 floppyDrive = strtok(NULL, ":");
296 }
297 }
298 else
299 {
300 // we assume that a floppy is always /dev/fd[x] with x from 0 to 7
301 char devName[10];
302 for (int i = 0; i <= 7; i++)
303 {
304 sprintf(devName, "/dev/fd%d", i);
305 if (validateDevice(devName, false))
306 {
307 ComObjPtr <HostFloppyDrive> hostFloppyDriveObj;
308 hostFloppyDriveObj.createObject();
309 hostFloppyDriveObj->init (Bstr (devName));
310 list.push_back (hostFloppyDriveObj);
311 }
312 }
313 }
314#else
315 /* PORTME */
316#endif
317
318 ComObjPtr<HostFloppyDriveCollection> collection;
319 collection.createObject();
320 collection->init (list);
321 collection.queryInterfaceTo(drives);
322 return S_OK;
323}
324
325#ifdef __WIN__
326
327static bool IsTAPDevice(const char *guid)
328{
329 HKEY hNetcard;
330 LONG status;
331 DWORD len;
332 int i = 0;
333 bool ret = false;
334
335 status = RegOpenKeyExA(HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E972-E325-11CE-BFC1-08002BE10318}", 0, KEY_READ, &hNetcard);
336 if (status != ERROR_SUCCESS)
337 return false;
338
339 while(true)
340 {
341 char szEnumName[256];
342 char szNetCfgInstanceId[256];
343 DWORD dwKeyType;
344 HKEY hNetCardGUID;
345
346 len = sizeof(szEnumName);
347 status = RegEnumKeyExA(hNetcard, i, szEnumName, &len, NULL, NULL, NULL, NULL);
348 if (status != ERROR_SUCCESS)
349 break;
350
351 status = RegOpenKeyExA(hNetcard, szEnumName, 0, KEY_READ, &hNetCardGUID);
352 if (status == ERROR_SUCCESS)
353 {
354 len = sizeof (szNetCfgInstanceId);
355 status = RegQueryValueExA(hNetCardGUID, "NetCfgInstanceId", NULL, &dwKeyType, (LPBYTE)szNetCfgInstanceId, &len);
356 if (status == ERROR_SUCCESS && dwKeyType == REG_SZ)
357 {
358 char szNetProductName[256];
359 char szNetProviderName[256];
360
361 szNetProductName[0] = 0;
362 len = sizeof(szNetProductName);
363 status = RegQueryValueExA(hNetCardGUID, "ProductName", NULL, &dwKeyType, (LPBYTE)szNetProductName, &len);
364
365 szNetProviderName[0] = 0;
366 len = sizeof(szNetProviderName);
367 status = RegQueryValueExA(hNetCardGUID, "ProviderName", NULL, &dwKeyType, (LPBYTE)szNetProviderName, &len);
368
369 if ( !strcmp(szNetCfgInstanceId, guid)
370 && !strcmp(szNetProductName, "VirtualBox TAP Adapter")
371 && !strcmp(szNetProviderName, "InnoTek Systemberatung GmbH"))
372 {
373 ret = true;
374 RegCloseKey(hNetCardGUID);
375 break;
376 }
377 }
378 RegCloseKey(hNetCardGUID);
379 }
380 ++i;
381 }
382
383 RegCloseKey (hNetcard);
384 return ret;
385}
386
387/**
388 * Returns a list of host network interfaces.
389 *
390 * @returns COM status code
391 * @param drives address of result pointer
392 */
393STDMETHODIMP Host::COMGETTER(NetworkInterfaces) (IHostNetworkInterfaceCollection **networkInterfaces)
394{
395 if (!networkInterfaces)
396 return E_POINTER;
397 AutoLock lock(this);
398 CHECK_READY();
399
400 std::list <ComObjPtr <HostNetworkInterface> > list;
401
402 HKEY hCtrlNet;
403 LONG status;
404 DWORD len;
405 int i = 0;
406 status = RegOpenKeyExA(HKEY_LOCAL_MACHINE,
407 "SYSTEM\\CurrentControlSet\\Control\\Network\\{4D36E972-E325-11CE-BFC1-08002BE10318}",
408 0, KEY_READ, &hCtrlNet);
409 if (status != ERROR_SUCCESS)
410 return setError(E_FAIL, tr("Could not open registry key"));
411
412 while (true)
413 {
414 char szNetworkGUID[256];
415 HKEY hConnection;
416 char szNetworkConnection[256];
417
418 len = sizeof (szNetworkGUID);
419 status = RegEnumKeyExA(hCtrlNet, i, szNetworkGUID, &len, NULL, NULL, NULL, NULL);
420 if (status != ERROR_SUCCESS)
421 break;
422
423 RTStrPrintf(szNetworkConnection, sizeof(szNetworkConnection), "%s\\Connection", szNetworkGUID);
424 status = RegOpenKeyExA(hCtrlNet, szNetworkConnection, 0, KEY_READ, &hConnection);
425 if (status == ERROR_SUCCESS)
426 {
427 DWORD dwKeyType;
428
429 len = sizeof (szNetworkConnection);
430 status = RegQueryValueExA(hConnection, "Name", NULL, &dwKeyType, (LPBYTE)szNetworkConnection, &len);
431 if (status == ERROR_SUCCESS && dwKeyType == REG_SZ)
432 {
433 if (IsTAPDevice(szNetworkGUID))
434 {
435 /* create a new object and add it to the list */
436 ComObjPtr<HostNetworkInterface> networkInterfaceObj;
437 networkInterfaceObj.createObject();
438 /* remove the curly bracket at the end */
439 szNetworkGUID[strlen(szNetworkGUID) - 1] = '\0';
440 if (SUCCEEDED(networkInterfaceObj->init(Bstr(szNetworkConnection), Guid(szNetworkGUID + 1))))
441 list.push_back(networkInterfaceObj);
442 }
443 }
444 RegCloseKey (hConnection);
445 }
446 ++i;
447 }
448 RegCloseKey (hCtrlNet);
449
450 ComObjPtr<HostNetworkInterfaceCollection> collection;
451 collection.createObject();
452 collection->init(list);
453 collection.queryInterfaceTo(networkInterfaces);
454 return S_OK;
455}
456#endif /* __WIN__ */
457
458STDMETHODIMP Host::COMGETTER(USBDevices)(IHostUSBDeviceCollection **aUSBDevices)
459{
460#ifdef VBOX_WITH_USB
461 if (!aUSBDevices)
462 return E_POINTER;
463
464 AutoLock alock (this);
465 CHECK_READY();
466
467 HRESULT rc = checkUSBProxyService();
468 CheckComRCReturnRC (rc);
469
470 ComObjPtr <HostUSBDeviceCollection> collection;
471 collection.createObject();
472 collection->init (mUSBDevices);
473 collection.queryInterfaceTo (aUSBDevices);
474 return S_OK;
475#else
476 /* Note: The GUI depends on this method returning E_NOTIMPL with no
477 * extended error info to indicate that USB is simply not available
478 * (w/o treting it as a failure), for example, as in OSE */
479 return E_NOTIMPL;
480#endif
481}
482
483STDMETHODIMP Host::COMGETTER(USBDeviceFilters) (IHostUSBDeviceFilterCollection ** aUSBDeviceFilters)
484{
485#ifdef VBOX_WITH_USB
486 if (!aUSBDeviceFilters)
487 return E_POINTER;
488
489 AutoLock alock (this);
490 CHECK_READY();
491
492 HRESULT rc = checkUSBProxyService();
493 CheckComRCReturnRC (rc);
494
495 ComObjPtr <HostUSBDeviceFilterCollection> collection;
496 collection.createObject();
497 collection->init (mUSBDeviceFilters);
498 collection.queryInterfaceTo (aUSBDeviceFilters);
499 return S_OK;
500#else
501 /* Note: The GUI depends on this method returning E_NOTIMPL with no
502 * extended error info to indicate that USB is simply not available
503 * (w/o treting it as a failure), for example, as in OSE */
504 return E_NOTIMPL;
505#endif
506}
507
508/**
509 * Returns the number of installed logical processors
510 *
511 * @returns COM status code
512 * @param count address of result variable
513 */
514STDMETHODIMP Host::COMGETTER(ProcessorCount)(ULONG *count)
515{
516 if (!count)
517 return E_POINTER;
518 AutoLock lock(this);
519 CHECK_READY();
520 *count = RTSystemProcessorGetCount();
521 return S_OK;
522}
523
524/**
525 * Returns the (approximate) speed of the host CPU in MHz
526 *
527 * @returns COM status code
528 * @param speed address of result variable
529 */
530STDMETHODIMP Host::COMGETTER(ProcessorSpeed)(ULONG *speed)
531{
532 if (!speed)
533 return E_POINTER;
534 AutoLock lock(this);
535 CHECK_READY();
536 /** @todo Add a runtime function for this which uses GIP. */
537 return S_OK;
538}
539/**
540 * Returns a description string for the host CPU
541 *
542 * @returns COM status code
543 * @param description address of result variable
544 */
545STDMETHODIMP Host::COMGETTER(ProcessorDescription)(BSTR *description)
546{
547 if (!description)
548 return E_POINTER;
549 AutoLock lock(this);
550 CHECK_READY();
551 /** @todo */
552 return S_OK;
553}
554
555
556/**
557 * Returns the amount of installed system memory in megabytes
558 *
559 * @returns COM status code
560 * @param size address of result variable
561 */
562STDMETHODIMP Host::COMGETTER(MemorySize)(ULONG *size)
563{
564 if (!size)
565 return E_POINTER;
566 AutoLock lock(this);
567 CHECK_READY();
568 /** @todo */
569 return S_OK;
570}
571
572/**
573 * Returns the current system memory free space in megabytes
574 *
575 * @returns COM status code
576 * @param available address of result variable
577 */
578STDMETHODIMP Host::COMGETTER(MemoryAvailable)(ULONG *available)
579{
580 if (!available)
581 return E_POINTER;
582 AutoLock lock(this);
583 CHECK_READY();
584 /** @todo */
585 return S_OK;
586}
587
588/**
589 * Returns the name string of the host operating system
590 *
591 * @returns COM status code
592 * @param os address of result variable
593 */
594STDMETHODIMP Host::COMGETTER(OperatingSystem)(BSTR *os)
595{
596 if (!os)
597 return E_POINTER;
598 AutoLock lock(this);
599 CHECK_READY();
600 /** @todo */
601 return S_OK;
602}
603
604/**
605 * Returns the version string of the host operating system
606 *
607 * @returns COM status code
608 * @param os address of result variable
609 */
610STDMETHODIMP Host::COMGETTER(OSVersion)(BSTR *version)
611{
612 if (!version)
613 return E_POINTER;
614 AutoLock lock(this);
615 CHECK_READY();
616 /** @todo */
617 return S_OK;
618}
619
620/**
621 * Returns the current host time in milliseconds since 1970-01-01 UTC.
622 *
623 * @returns COM status code
624 * @param time address of result variable
625 */
626STDMETHODIMP Host::COMGETTER(UTCTime)(LONG64 *aUTCTime)
627{
628 if (!aUTCTime)
629 return E_POINTER;
630 AutoLock lock(this);
631 CHECK_READY();
632 RTTIMESPEC now;
633 *aUTCTime = RTTimeSpecGetMilli(RTTimeNow(&now));
634 return S_OK;
635}
636
637// IHost methods
638////////////////////////////////////////////////////////////////////////////////
639
640#ifdef __WIN__
641
642/**
643 * Returns TRUE if the Windows version is 6.0 or greater (i.e. it's Vista and
644 * later OSes) and it has the UAC (User Account Control) feature enabled.
645 */
646static BOOL IsUACEnabled()
647{
648 LONG rc = 0;
649
650 OSVERSIONINFOEX info;
651 ZeroMemory (&info, sizeof (OSVERSIONINFOEX));
652 info.dwOSVersionInfoSize = sizeof (OSVERSIONINFOEX);
653 rc = GetVersionEx ((OSVERSIONINFO *) &info);
654 AssertReturn (rc != 0, FALSE);
655
656 LogFlowFunc (("dwMajorVersion=%d, dwMinorVersion=%d\n",
657 info.dwMajorVersion, info.dwMinorVersion));
658
659 /* we are interested only in Vista (and newer versions...). In all
660 * earlier versions UAC is not present. */
661 if (info.dwMajorVersion < 6)
662 return FALSE;
663
664 /* the default EnableLUA value is 1 (Enabled) */
665 DWORD dwEnableLUA = 1;
666
667 HKEY hKey;
668 rc = RegOpenKeyExA (HKEY_LOCAL_MACHINE,
669 "Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System",
670 0, KEY_QUERY_VALUE, &hKey);
671
672 Assert (rc == ERROR_SUCCESS || rc == ERROR_PATH_NOT_FOUND);
673 if (rc == ERROR_SUCCESS)
674 {
675
676 DWORD cbEnableLUA = sizeof (dwEnableLUA);
677 rc = RegQueryValueExA (hKey, "EnableLUA", NULL, NULL,
678 (LPBYTE) &dwEnableLUA, &cbEnableLUA);
679
680 RegCloseKey (hKey);
681
682 Assert (rc == ERROR_SUCCESS || rc == ERROR_FILE_NOT_FOUND);
683 }
684
685 LogFlowFunc (("rc=%d, dwEnableLUA=%d\n", rc, dwEnableLUA));
686
687 return dwEnableLUA == 1;
688}
689
690struct NetworkInterfaceHelperClientData
691{
692 SVCHlpMsg::Code msgCode;
693 /* for SVCHlpMsg::CreateHostNetworkInterface */
694 Bstr name;
695 ComObjPtr <HostNetworkInterface> iface;
696 /* for SVCHlpMsg::RemoveHostNetworkInterface */
697 Guid guid;
698};
699
700STDMETHODIMP
701Host::CreateHostNetworkInterface (INPTR BSTR aName,
702 IHostNetworkInterface **aHostNetworkInterface,
703 IProgress **aProgress)
704{
705 if (!aName)
706 return E_INVALIDARG;
707 if (!aHostNetworkInterface)
708 return E_POINTER;
709 if (!aProgress)
710 return E_POINTER;
711
712 AutoLock lock (this);
713 CHECK_READY();
714
715 HRESULT rc = S_OK;
716
717 /* first check whether an interface with the given name already exists */
718 {
719 ComPtr <IHostNetworkInterfaceCollection> coll;
720 rc = COMGETTER(NetworkInterfaces) (coll.asOutParam());
721 CheckComRCReturnRC (rc);
722 ComPtr <IHostNetworkInterface> iface;
723 if (SUCCEEDED (coll->FindByName (aName, iface.asOutParam())))
724 return setError (E_FAIL,
725 tr ("Host network interface '%ls' already exists"), aName);
726 }
727
728 /* create a progress object */
729 ComObjPtr <Progress> progress;
730 progress.createObject();
731 rc = progress->init (mParent, (IHost *) this,
732 Bstr (tr ("Creating host network interface")),
733 FALSE /* aCancelable */);
734 CheckComRCReturnRC (rc);
735 progress.queryInterfaceTo (aProgress);
736
737 /* create a new uninitialized host interface object */
738 ComObjPtr <HostNetworkInterface> iface;
739 iface.createObject();
740 iface.queryInterfaceTo (aHostNetworkInterface);
741
742 /* create the networkInterfaceHelperClient() argument */
743 std::auto_ptr <NetworkInterfaceHelperClientData>
744 d (new NetworkInterfaceHelperClientData());
745 AssertReturn (d.get(), E_OUTOFMEMORY);
746
747 d->msgCode = SVCHlpMsg::CreateHostNetworkInterface;
748 d->name = aName;
749 d->iface = iface;
750
751 rc = mParent->startSVCHelperClient (
752 IsUACEnabled() == TRUE /* aPrivileged */,
753 networkInterfaceHelperClient,
754 static_cast <void *> (d.get()),
755 progress);
756
757 if (SUCCEEDED (rc))
758 {
759 /* d is now owned by networkInterfaceHelperClient(), so release it */
760 d.release();
761 }
762
763 return rc;
764}
765
766STDMETHODIMP
767Host::RemoveHostNetworkInterface (INPTR GUIDPARAM aId,
768 IHostNetworkInterface **aHostNetworkInterface,
769 IProgress **aProgress)
770{
771 if (!aHostNetworkInterface)
772 return E_POINTER;
773 if (!aProgress)
774 return E_POINTER;
775
776 AutoLock lock (this);
777 CHECK_READY();
778
779 HRESULT rc = S_OK;
780
781 /* first check whether an interface with the given name already exists */
782 {
783 ComPtr <IHostNetworkInterfaceCollection> coll;
784 rc = COMGETTER(NetworkInterfaces) (coll.asOutParam());
785 CheckComRCReturnRC (rc);
786 ComPtr <IHostNetworkInterface> iface;
787 if (FAILED (coll->FindById (aId, iface.asOutParam())))
788 return setError (E_FAIL,
789 tr ("Host network interface with UUID {%Vuuid} does not exist"),
790 Guid (aId).raw());
791
792 /* return the object to be removed to the caller */
793 iface.queryInterfaceTo (aHostNetworkInterface);
794 }
795
796 /* create a progress object */
797 ComObjPtr <Progress> progress;
798 progress.createObject();
799 rc = progress->init (mParent, (IHost *) this,
800 Bstr (tr ("Removing host network interface")),
801 FALSE /* aCancelable */);
802 CheckComRCReturnRC (rc);
803 progress.queryInterfaceTo (aProgress);
804
805 /* create the networkInterfaceHelperClient() argument */
806 std::auto_ptr <NetworkInterfaceHelperClientData>
807 d (new NetworkInterfaceHelperClientData());
808 AssertReturn (d.get(), E_OUTOFMEMORY);
809
810 d->msgCode = SVCHlpMsg::RemoveHostNetworkInterface;
811 d->guid = aId;
812
813 rc = mParent->startSVCHelperClient (
814 IsUACEnabled() == TRUE /* aPrivileged */,
815 networkInterfaceHelperClient,
816 static_cast <void *> (d.get()),
817 progress);
818
819 if (SUCCEEDED (rc))
820 {
821 /* d is now owned by networkInterfaceHelperClient(), so release it */
822 d.release();
823 }
824
825 return rc;
826}
827
828#endif /* __WIN__ */
829
830STDMETHODIMP Host::CreateUSBDeviceFilter (INPTR BSTR aName, IHostUSBDeviceFilter **aFilter)
831{
832#ifdef VBOX_WITH_USB
833 if (!aFilter)
834 return E_POINTER;
835
836 if (!aName || *aName == 0)
837 return E_INVALIDARG;
838
839 AutoLock lock (this);
840 CHECK_READY();
841
842 HRESULT rc = checkUSBProxyService();
843 CheckComRCReturnRC (rc);
844
845 ComObjPtr <HostUSBDeviceFilter> filter;
846 filter.createObject();
847 rc = filter->init (this, aName);
848 ComAssertComRCRet (rc, rc);
849 rc = filter.queryInterfaceTo (aFilter);
850 AssertComRCReturn (rc, rc);
851 return S_OK;
852#else
853 /* Note: The GUI depends on this method returning E_NOTIMPL with no
854 * extended error info to indicate that USB is simply not available
855 * (w/o treting it as a failure), for example, as in OSE */
856 return E_NOTIMPL;
857#endif
858}
859
860STDMETHODIMP Host::InsertUSBDeviceFilter (ULONG aPosition, IHostUSBDeviceFilter *aFilter)
861{
862#ifdef VBOX_WITH_USB
863 if (!aFilter)
864 return E_INVALIDARG;
865
866 AutoLock alock (this);
867 CHECK_READY();
868
869 HRESULT rc = checkUSBProxyService();
870 CheckComRCReturnRC (rc);
871
872 ComObjPtr <HostUSBDeviceFilter> filter = getDependentChild (aFilter);
873 if (!filter)
874 return setError (E_INVALIDARG,
875 tr ("The given USB device filter is not created within "
876 "this VirtualBox instance"));
877
878 if (filter->mInList)
879 return setError (E_INVALIDARG,
880 tr ("The given USB device filter is already in the list"));
881
882 /* iterate to the position... */
883 USBDeviceFilterList::iterator it = mUSBDeviceFilters.begin();
884 std::advance (it, aPosition);
885 /* ...and insert */
886 mUSBDeviceFilters.insert (it, filter);
887 filter->mInList = true;
888
889 /* notify the proxy (only when the filter is active) */
890 if (filter->data().mActive)
891 {
892 ComAssertRet (filter->id() == NULL, E_FAIL);
893 filter->id() =
894 mUSBProxyService->insertFilter (ComPtr <IUSBDeviceFilter> (aFilter));
895 }
896
897 /* save the global settings */
898 alock.unlock();
899 return mParent->saveSettings();
900#else
901 /* Note: The GUI depends on this method returning E_NOTIMPL with no
902 * extended error info to indicate that USB is simply not available
903 * (w/o treting it as a failure), for example, as in OSE */
904 return E_NOTIMPL;
905#endif
906}
907
908STDMETHODIMP Host::RemoveUSBDeviceFilter (ULONG aPosition, IHostUSBDeviceFilter **aFilter)
909{
910#ifdef VBOX_WITH_USB
911 if (!aFilter)
912 return E_POINTER;
913
914 AutoLock alock (this);
915 CHECK_READY();
916
917 HRESULT rc = checkUSBProxyService();
918 CheckComRCReturnRC (rc);
919
920 if (!mUSBDeviceFilters.size())
921 return setError (E_INVALIDARG,
922 tr ("The USB device filter list is empty"));
923
924 if (aPosition >= mUSBDeviceFilters.size())
925 return setError (E_INVALIDARG,
926 tr ("Invalid position: %lu (must be in range [0, %lu])"),
927 aPosition, mUSBDeviceFilters.size() - 1);
928
929 ComObjPtr <HostUSBDeviceFilter> filter;
930 {
931 /* iterate to the position... */
932 USBDeviceFilterList::iterator it = mUSBDeviceFilters.begin();
933 std::advance (it, aPosition);
934 /* ...get an element from there... */
935 filter = *it;
936 /* ...and remove */
937 filter->mInList = false;
938 mUSBDeviceFilters.erase (it);
939 }
940
941 filter.queryInterfaceTo (aFilter);
942
943 /* notify the proxy (only when the filter is active) */
944 if (filter->data().mActive)
945 {
946 ComAssertRet (filter->id() != NULL, E_FAIL);
947 mUSBProxyService->removeFilter (filter->id());
948 filter->id() = NULL;
949 }
950
951 /* save the global settings */
952 alock.unlock();
953 return mParent->saveSettings();
954#else
955 /* Note: The GUI depends on this method returning E_NOTIMPL with no
956 * extended error info to indicate that USB is simply not available
957 * (w/o treting it as a failure), for example, as in OSE */
958 return E_NOTIMPL;
959#endif
960}
961
962// public methods only for internal purposes
963////////////////////////////////////////////////////////////////////////////////
964
965/**
966 * Called by setter methods of all USB device filters.
967 */
968HRESULT Host::onUSBDeviceFilterChange (HostUSBDeviceFilter *aFilter,
969 BOOL aActiveChanged /* = FALSE */)
970{
971 AutoLock alock (this);
972 CHECK_READY();
973
974 if (aFilter->mInList)
975 {
976 if (aActiveChanged)
977 {
978 // insert/remove the filter from the proxy
979 if (aFilter->data().mActive)
980 {
981 ComAssertRet (aFilter->id() == NULL, E_FAIL);
982 aFilter->id() =
983 mUSBProxyService->insertFilter (ComPtr <IUSBDeviceFilter> (aFilter));
984 }
985 else
986 {
987 ComAssertRet (aFilter->id() != NULL, E_FAIL);
988 mUSBProxyService->removeFilter (aFilter->id());
989 aFilter->id() = NULL;
990 }
991 }
992 else
993 {
994 if (aFilter->data().mActive)
995 {
996 // update the filter in the proxy
997 ComAssertRet (aFilter->id() != NULL, E_FAIL);
998 mUSBProxyService->removeFilter (aFilter->id());
999 aFilter->id() =
1000 mUSBProxyService->insertFilter (ComPtr <IUSBDeviceFilter> (aFilter));
1001 }
1002 }
1003
1004 // save the global settings... yeah, on every single filter property change
1005 alock.unlock();
1006 return mParent->saveSettings();
1007 }
1008
1009 return S_OK;
1010}
1011
1012HRESULT Host::loadSettings (CFGNODE aGlobal)
1013{
1014 AutoLock lock (this);
1015 CHECK_READY();
1016
1017 ComAssertRet (aGlobal, E_FAIL);
1018
1019 CFGNODE filters = NULL;
1020 CFGLDRGetChildNode (aGlobal, "USBDeviceFilters", 0, &filters);
1021 Assert (filters);
1022
1023 HRESULT rc = S_OK;
1024
1025 unsigned filterCount = 0;
1026 CFGLDRCountChildren (filters, "DeviceFilter", &filterCount);
1027 for (unsigned i = 0; i < filterCount && SUCCEEDED (rc); i++)
1028 {
1029 CFGNODE filter = NULL;
1030 CFGLDRGetChildNode (filters, "DeviceFilter", i, &filter);
1031 Assert (filter);
1032
1033 Bstr name;
1034 CFGLDRQueryBSTR (filter, "name", name.asOutParam());
1035 bool active;
1036 CFGLDRQueryBool (filter, "active", &active);
1037
1038 Bstr vendorId;
1039 CFGLDRQueryBSTR (filter, "vendorid", vendorId.asOutParam());
1040 Bstr productId;
1041 CFGLDRQueryBSTR (filter, "productid", productId.asOutParam());
1042 Bstr revision;
1043 CFGLDRQueryBSTR (filter, "revision", revision.asOutParam());
1044 Bstr manufacturer;
1045 CFGLDRQueryBSTR (filter, "manufacturer", manufacturer.asOutParam());
1046 Bstr product;
1047 CFGLDRQueryBSTR (filter, "product", product.asOutParam());
1048 Bstr serialNumber;
1049 CFGLDRQueryBSTR (filter, "serialnumber", serialNumber.asOutParam());
1050 Bstr port;
1051 CFGLDRQueryBSTR (filter, "port", port.asOutParam());
1052
1053 USBDeviceFilterAction_T action;
1054 action = USBDeviceFilterAction_USBDeviceFilterIgnore;
1055 Bstr actionStr;
1056 CFGLDRQueryBSTR (filter, "action", actionStr.asOutParam());
1057 if (actionStr == L"Ignore")
1058 action = USBDeviceFilterAction_USBDeviceFilterIgnore;
1059 else
1060 if (actionStr == L"Hold")
1061 action = USBDeviceFilterAction_USBDeviceFilterHold;
1062 else
1063 AssertMsgFailed (("Invalid action: %ls\n", actionStr.raw()));
1064
1065 ComObjPtr <HostUSBDeviceFilter> filterObj;
1066 filterObj.createObject();
1067 rc = filterObj->init (this,
1068 name, active, vendorId, productId, revision,
1069 manufacturer, product, serialNumber, port,
1070 action);
1071 // error info is set by init() when appropriate
1072 if (SUCCEEDED (rc))
1073 {
1074 mUSBDeviceFilters.push_back (filterObj);
1075 filterObj->mInList = true;
1076
1077 // notify the proxy (only when the filter is active)
1078 if (filterObj->data().mActive)
1079 {
1080 HostUSBDeviceFilter *flt = filterObj; // resolve ambiguity
1081 flt->id() =
1082 mUSBProxyService->insertFilter (ComPtr <IUSBDeviceFilter> (flt));
1083 }
1084 }
1085
1086 CFGLDRReleaseNode (filter);
1087 }
1088
1089 CFGLDRReleaseNode (filters);
1090
1091 return rc;
1092}
1093
1094HRESULT Host::saveSettings (CFGNODE aGlobal)
1095{
1096 AutoLock lock (this);
1097 CHECK_READY();
1098
1099 ComAssertRet (aGlobal, E_FAIL);
1100
1101 // first, delete the entry
1102 CFGNODE filters = NULL;
1103 int vrc = CFGLDRGetChildNode (aGlobal, "USBDeviceFilters", 0, &filters);
1104 if (VBOX_SUCCESS (vrc))
1105 {
1106 vrc = CFGLDRDeleteNode (filters);
1107 ComAssertRCRet (vrc, E_FAIL);
1108 }
1109 // then, recreate it
1110 vrc = CFGLDRCreateChildNode (aGlobal, "USBDeviceFilters", &filters);
1111 ComAssertRCRet (vrc, E_FAIL);
1112
1113 USBDeviceFilterList::const_iterator it = mUSBDeviceFilters.begin();
1114 while (it != mUSBDeviceFilters.end())
1115 {
1116 AutoLock filterLock (*it);
1117 const HostUSBDeviceFilter::Data &data = (*it)->data();
1118
1119 CFGNODE filter = NULL;
1120 CFGLDRAppendChildNode (filters, "DeviceFilter", &filter);
1121
1122 CFGLDRSetBSTR (filter, "name", data.mName);
1123 CFGLDRSetBool (filter, "active", !!data.mActive);
1124
1125 // all are optional
1126 if (data.mVendorId.string())
1127 CFGLDRSetBSTR (filter, "vendorid", data.mVendorId.string());
1128 if (data.mProductId.string())
1129 CFGLDRSetBSTR (filter, "productid", data.mProductId.string());
1130 if (data.mRevision.string())
1131 CFGLDRSetBSTR (filter, "revision", data.mRevision.string());
1132 if (data.mManufacturer.string())
1133 CFGLDRSetBSTR (filter, "manufacturer", data.mManufacturer.string());
1134 if (data.mProduct.string())
1135 CFGLDRSetBSTR (filter, "product", data.mProduct.string());
1136 if (data.mSerialNumber.string())
1137 CFGLDRSetBSTR (filter, "serialnumber", data.mSerialNumber.string());
1138 if (data.mPort.string())
1139 CFGLDRSetBSTR (filter, "port", data.mPort.string());
1140
1141 // action is mandatory
1142 if (data.mAction == USBDeviceFilterAction_USBDeviceFilterIgnore)
1143 CFGLDRSetString (filter, "action", "Ignore");
1144 else
1145 if (data.mAction == USBDeviceFilterAction_USBDeviceFilterHold)
1146 CFGLDRSetString (filter, "action", "Hold");
1147 else
1148 AssertMsgFailed (("Invalid action: %d\n", data.mAction));
1149
1150 CFGLDRReleaseNode (filter);
1151
1152 ++ it;
1153 }
1154
1155 CFGLDRReleaseNode (filters);
1156
1157 return S_OK;
1158}
1159
1160/**
1161 * Marks the given host USB device as captured by the given machine and returns
1162 * it as IUSBDevice to the caller.
1163 *
1164 * Called by Console from the VM process (throug IInternalMachineControl).
1165 * Must return extended error info in case of errors.
1166 */
1167HRESULT Host::captureUSBDevice (SessionMachine *aMachine, INPTR GUIDPARAM aId,
1168 IUSBDevice **aHostDevice)
1169{
1170 ComAssertRet (aMachine, E_INVALIDARG);
1171 ComAssertRet (aHostDevice, E_POINTER);
1172
1173 AutoLock lock (this);
1174 CHECK_READY();
1175
1176 Guid id (aId);
1177
1178 ComObjPtr <HostUSBDevice> device;
1179 USBDeviceList::iterator it = mUSBDevices.begin();
1180 while (!device && it != mUSBDevices.end())
1181 {
1182 if ((*it)->id() == id)
1183 device = (*it);
1184 ++ it;
1185 }
1186
1187 if (!device)
1188 return setError (E_INVALIDARG,
1189 tr ("USB device with UUID {%s} is not currently attached to the host"),
1190 id.toString().raw());
1191
1192 if (device->state() == USBDeviceState_USBDeviceNotSupported)
1193 return setError (E_INVALIDARG,
1194 tr ("USB device '%s' with UUID {%s} cannot be accessed by guest "
1195 "computers"),
1196 device->name().raw(), id.toString().raw());
1197
1198 if (device->state() == USBDeviceState_USBDeviceUnavailable)
1199 return setError (E_INVALIDARG,
1200 tr ("USB device '%s' with UUID {%s} is being exclusively used by the "
1201 "host computer"),
1202 device->name().raw(), id.toString().raw());
1203
1204 if (device->state() == USBDeviceState_USBDeviceCaptured)
1205 return setError (E_INVALIDARG,
1206 tr ("USB device '%s' with UUID {%s} is already captured by the virtual "
1207 "machine '%ls'"),
1208 device->name().raw(), id.toString().raw(),
1209 aMachine->userData()->mName.raw());
1210
1211 // try capture the device
1212 bool ok = device->setCaptured (aMachine);
1213 if (!ok)
1214 {
1215 if (device->state() == USBDeviceState_USBDeviceBusy)
1216 return setError (E_FAIL,
1217 tr ("USB device with UUID {%s} is being accessed by the host "
1218 "computer and cannot be attached to the virtual machine."
1219 "Please try later"),
1220 id.toString().raw());
1221 else
1222 ComAssertRet (ok, E_FAIL);
1223 }
1224
1225 // return the device to the caller as IUSBDevice
1226 device.queryInterfaceTo (aHostDevice);
1227 return S_OK;
1228}
1229
1230/**
1231 * Replays all filters against the given USB device excluding filters of the
1232 * machine the device is currently marked as captured by.
1233 *
1234 * Called by Console from the VM process (throug IInternalMachineControl).
1235 */
1236HRESULT Host::releaseUSBDevice (SessionMachine *aMachine, INPTR GUIDPARAM aId)
1237{
1238 AutoLock lock (this);
1239 CHECK_READY();
1240
1241 ComObjPtr <HostUSBDevice> device;
1242 USBDeviceList::iterator it = mUSBDevices.begin();
1243 while (!device && it != mUSBDevices.end())
1244 {
1245 if ((*it)->id() == aId)
1246 device = (*it);
1247 ++ it;
1248 }
1249
1250 ComAssertRet (!!device, E_FAIL);
1251 ComAssertRet (device->machine() == aMachine, E_FAIL);
1252
1253 // reset the device and apply filters
1254 int vrc = device->reset();
1255 ComAssertRCRet (vrc, E_FAIL);
1256
1257 HRESULT rc = applyAllUSBFilters (device, aMachine);
1258 ComAssertComRC (rc);
1259
1260 return rc;
1261}
1262
1263/**
1264 * Runs the filters of the given machine against all currently available USB
1265 * devices, marks those that match as captured and returns them as the colection
1266 * of IUSBDevice instances.
1267 *
1268 * Called by Console from the VM process (through IInternalMachineControl).
1269 */
1270HRESULT Host::autoCaptureUSBDevices (SessionMachine *aMachine,
1271 IUSBDeviceCollection **aHostDevices)
1272{
1273 LogFlowMember (("Host::autoCaptureUSBDevices: aMachine=%p\n", aMachine));
1274
1275 AutoLock lock (this);
1276 CHECK_READY();
1277
1278 std::list <ComPtr <IUSBDevice> > list;
1279
1280 USBDeviceList::iterator it = mUSBDevices.begin();
1281 while (it != mUSBDevices.end())
1282 {
1283 ComObjPtr <HostUSBDevice> device = *it;
1284 if (device->isIgnored())
1285 continue;
1286
1287 if (device->state() == USBDeviceState_USBDeviceBusy ||
1288 device->state() == USBDeviceState_USBDeviceAvailable ||
1289 device->state() == USBDeviceState_USBDeviceHeld)
1290 {
1291 applyMachineUSBFilters (aMachine, device);
1292
1293 if (device->state() == USBDeviceState_USBDeviceCaptured)
1294 {
1295 // put the device to the return list
1296 ComPtr <IUSBDevice> d;
1297 device.queryInterfaceTo (d.asOutParam());
1298 Assert (!d.isNull());
1299 list.push_back (d);
1300 }
1301 }
1302 ++ it;
1303 }
1304
1305 ComObjPtr <IfaceUSBDeviceCollection> coll;
1306 coll.createObject();
1307 coll->init (list);
1308 coll.queryInterfaceTo (aHostDevices);
1309
1310 return S_OK;
1311}
1312
1313/**
1314 * Replays all filters against all USB devices currently marked as captured
1315 * by the given machine (excluding this machine's filters).
1316 *
1317 * Called by Console from the VM process (throug IInternalMachineControl).
1318 */
1319HRESULT Host::releaseAllUSBDevices (SessionMachine *aMachine)
1320{
1321 AutoLock lock (this);
1322 CHECK_READY();
1323
1324 USBDeviceList::iterator it = mUSBDevices.begin();
1325 while (it != mUSBDevices.end())
1326 {
1327 ComObjPtr <HostUSBDevice> device = *it;
1328 if (device->machine() == aMachine)
1329 {
1330 // reset the device and apply filters
1331 device->reset();
1332 HRESULT rc = applyAllUSBFilters (device, aMachine);
1333 ComAssertComRC (rc);
1334 }
1335 ++ it;
1336 }
1337
1338 return S_OK;
1339}
1340
1341// private methods
1342////////////////////////////////////////////////////////////////////////////////
1343
1344#ifdef __LINUX__
1345/**
1346 * Helper function to parse the given mount file and add found entries
1347 */
1348void Host::parseMountTable(char *mountTable, std::list <ComObjPtr <HostDVDDrive> > &list)
1349{
1350 FILE *mtab = setmntent(mountTable, "r");
1351 if (mtab)
1352 {
1353 struct mntent *mntent;
1354 char *mnt_type;
1355 char *mnt_dev;
1356 char *tmp;
1357 while ((mntent = getmntent(mtab)))
1358 {
1359 mnt_type = (char*)malloc(strlen(mntent->mnt_type) + 1);
1360 mnt_dev = (char*)malloc(strlen(mntent->mnt_fsname) + 1);
1361 strcpy(mnt_type, mntent->mnt_type);
1362 strcpy(mnt_dev, mntent->mnt_fsname);
1363 // supermount fs case
1364 if (strcmp(mnt_type, "supermount") == 0)
1365 {
1366 tmp = strstr(mntent->mnt_opts, "fs=");
1367 if (tmp)
1368 {
1369 free(mnt_type);
1370 mnt_type = strdup(tmp + strlen("fs="));
1371 if (mnt_type)
1372 {
1373 tmp = strchr(mnt_type, ',');
1374 if (tmp)
1375 {
1376 *tmp = '\0';
1377 }
1378 }
1379 }
1380 tmp = strstr(mntent->mnt_opts, "dev=");
1381 if (tmp)
1382 {
1383 free(mnt_dev);
1384 mnt_dev = strdup(tmp + strlen("dev="));
1385 if (mnt_dev)
1386 {
1387 tmp = strchr(mnt_dev, ',');
1388 if (tmp)
1389 {
1390 *tmp = '\0';
1391 }
1392 }
1393 }
1394 }
1395 if (strcmp(mnt_type, "iso9660") == 0)
1396 {
1397 /** @todo check whether we've already got the drive in our list! */
1398 if (validateDevice(mnt_dev, true))
1399 {
1400 ComObjPtr <HostDVDDrive> hostDVDDriveObj;
1401 hostDVDDriveObj.createObject();
1402 hostDVDDriveObj->init (Bstr (mnt_dev));
1403 list.push_back (hostDVDDriveObj);
1404 }
1405 }
1406 free(mnt_dev);
1407 free(mnt_type);
1408 }
1409 endmntent(mtab);
1410 }
1411}
1412
1413/**
1414 * Helper function to check whether the given device node is a valid drive
1415 */
1416bool Host::validateDevice(char *deviceNode, bool isCDROM)
1417{
1418 struct stat statInfo;
1419 bool retValue = false;
1420
1421 // sanity check
1422 if (!deviceNode)
1423 {
1424 return false;
1425 }
1426
1427 // first a simple stat() call
1428 if (stat(deviceNode, &statInfo) < 0)
1429 {
1430 return false;
1431 } else
1432 {
1433 if (isCDROM)
1434 {
1435 if (S_ISCHR(statInfo.st_mode) || S_ISBLK(statInfo.st_mode))
1436 {
1437 int fileHandle;
1438 // now try to open the device
1439 fileHandle = open(deviceNode, O_RDONLY | O_NONBLOCK, 0);
1440 if (fileHandle >= 0)
1441 {
1442 cdrom_subchnl cdChannelInfo;
1443 // this call will finally reveal the whole truth
1444 if ((ioctl(fileHandle, CDROMSUBCHNL, &cdChannelInfo) == 0) ||
1445 (errno == EIO) || (errno == ENOENT) ||
1446 (errno == EINVAL) || (errno == ENOMEDIUM))
1447 {
1448 retValue = true;
1449 }
1450 close(fileHandle);
1451 }
1452 }
1453 } else
1454 {
1455 // floppy case
1456 if (S_ISCHR(statInfo.st_mode) || S_ISBLK(statInfo.st_mode))
1457 {
1458 /// @todo do some more testing, maybe a nice IOCTL!
1459 retValue = true;
1460 }
1461 }
1462 }
1463 return retValue;
1464}
1465#endif // __LINUX__
1466
1467/**
1468 * Runs through all global filters to determine the state of the given
1469 * USB device, then unless ignored, runs trhough filters of all running machines
1470 * (excluding the given one) to automatically capture the device when there is a
1471 * match (the state of the device will be set to USBDeviceCaptured if so, and
1472 * the machine's process will be informed about the auto-capture).
1473 *
1474 * @param aDevice USB device to set state for
1475 * @param aMachine the machine whose filters are to be excluded (can be NULL)
1476 *
1477 * @note the method must be called from under this object's lock
1478 */
1479HRESULT Host::applyAllUSBFilters (ComObjPtr <HostUSBDevice> &aDevice,
1480 SessionMachine *aMachine /* = NULL */)
1481{
1482 LogFlowMember (("Host::applyAllUSBFilters: \n"));
1483
1484 // ignore unsupported devices
1485 if (aDevice->state() == USBDeviceState_USBDeviceNotSupported)
1486 return S_OK;
1487 // ignore unavailable devices as well
1488 if (aDevice->state() == USBDeviceState_USBDeviceUnavailable)
1489 return S_OK;
1490
1491 VirtualBox::SessionMachineVector machines;
1492 mParent->getOpenedMachines (machines);
1493
1494 // apply global filters
1495 USBDeviceFilterList::const_iterator it = mUSBDeviceFilters.begin();
1496 while (it != mUSBDeviceFilters.end())
1497 {
1498 AutoLock filterLock (*it);
1499 const HostUSBDeviceFilter::Data &data = (*it)->data();
1500 if (aDevice->isMatch (data))
1501 {
1502 if (data.mAction == USBDeviceFilterAction_USBDeviceFilterIgnore)
1503 return S_OK;
1504 if (data.mAction == USBDeviceFilterAction_USBDeviceFilterHold)
1505 break;
1506 }
1507 ++ it;
1508 }
1509
1510 // apply machine filters
1511 for (size_t i = 0; i < machines.size(); i++)
1512 {
1513 if (aMachine && machines [i] == aMachine)
1514 continue;
1515
1516 applyMachineUSBFilters (machines [i], aDevice);
1517
1518 if (aDevice->state() == USBDeviceState_USBDeviceCaptured)
1519 {
1520 // inform the machine's process about the auto-capture
1521 ComPtr <IUSBDevice> d;
1522 aDevice.queryInterfaceTo (d.asOutParam());
1523 HRESULT rc = machines [i]->onUSBDeviceAttach (d);
1524 if (SUCCEEDED(rc))
1525 return rc;
1526
1527 // the machine rejected it, continue applying filters.
1528 aDevice->reset();
1529 }
1530 }
1531
1532 // no machine filters were matched.
1533 // if no global filters were matched as well, release the device
1534 // to make it available on the host
1535 if ( it == mUSBDeviceFilters.end()
1536 && aDevice->state() == USBDeviceState_USBDeviceHeld)
1537 aDevice->setHostDriven();
1538/** @todo dmik, what about USBDeviceFilterHold??? */
1539/**
1540 * bird, everything's ok from what I see. if USBDeviceFilterHold filter was
1541 * matched, setHostDevice() will not (and should not) be called
1542 * (because it != mUSBDeviceFilters.end() in that case).
1543 */
1544
1545 return S_OK;
1546}
1547
1548/**
1549 * Runs through filters of the given machine in order to automatically capture
1550 * the given USB device when there is a match (the state of the device will
1551 * be set to USBDeviceCaptured if so, but the machine's process will *NOT* be)
1552 * informed about the auto-capture).
1553 *
1554 * @param aMachine the machine whose filters are to be run
1555 * @param aDevice USB device, a candidate for auto-attaching
1556 *
1557 * @note Locks aMachine for reading.
1558 */
1559void Host::applyMachineUSBFilters (SessionMachine *aMachine,
1560 ComObjPtr <HostUSBDevice> &aDevice)
1561{
1562 LogFlowThisFunc (("\n"));
1563
1564 AssertReturn (aMachine, (void) 0);
1565 AssertReturn (aDevice->state() != USBDeviceState_USBDeviceUnavailable, (void) 0);
1566
1567 /* We're going to use aMachine which is not our child/parent, add a caller */
1568 AutoCaller autoCaller (aMachine);
1569 if (!autoCaller.isOk())
1570 {
1571 /* silently return, the machine might be not running any more */
1572 return;
1573 }
1574
1575 /* enter the machine's lock because we want to access its USB controller */
1576 AutoReaderLock machineLock (aMachine);
1577
1578 if (aMachine->usbController()->hasMatchingFilter (aDevice))
1579 {
1580 /* try capture the device */
1581 bool ok = aDevice->setCaptured (aMachine);
1582
1583 /*
1584 * false is valid only when the state remains USBDeviceBusy meaning that
1585 * the device is currently busy (being accessed by the host)
1586 */
1587 Assert (ok || aDevice->state() == USBDeviceState_USBDeviceBusy);
1588 NOREF (ok);
1589 }
1590}
1591
1592/**
1593 * Called by USB proxy service when a new device is physically attached
1594 * to the host.
1595 *
1596 * @param aDevice Pointer to the device which has been attached.
1597 */
1598void Host::onUSBDeviceAttached (HostUSBDevice *aDevice)
1599{
1600 LogFlowMember (("Host::onUSBDeviceAttached: aDevice=%p\n", aDevice));
1601 /// @todo (dmik) check locks
1602 AutoLock alock (this);
1603
1604 // add to the collecion
1605 mUSBDevices.push_back (aDevice);
1606
1607 // apply all filters (no need to lock the device, nobody can access it yet)
1608 ComObjPtr <HostUSBDevice> DevPtr(aDevice);
1609 HRESULT rc = applyAllUSBFilters (DevPtr);
1610 AssertComRC (rc);
1611}
1612
1613/**
1614 * Called by USB proxy service (?) when the device is physically detached
1615 * from the host.
1616 *
1617 * @param aDevice Pointer to the device which has been detached.
1618 */
1619void Host::onUSBDeviceDetached (HostUSBDevice *aDevice)
1620{
1621 LogFlowMember (("Host::onUSBDeviceDetached: aDevice=%p\n", aDevice));
1622 /// @todo (dmik) check locks
1623 AutoLock alock (this);
1624
1625 Guid id = aDevice->id();
1626
1627 ComObjPtr <HostUSBDevice> device;
1628 Host::USBDeviceList::iterator it = mUSBDevices.begin();
1629 while (it != mUSBDevices.end())
1630 {
1631 if ((*it)->id() == id)
1632 {
1633 device = (*it);
1634 break;
1635 }
1636 ++ it;
1637 }
1638
1639 AssertReturn (!!device, (void) 0);
1640
1641 // remove from the collecion
1642 mUSBDevices.erase (it);
1643
1644 if (device->machine())
1645 {
1646 // the device is captured by a machine, instruct it to release
1647 alock.unlock();
1648 HRESULT rc = device->machine()->onUSBDeviceDetach (device->id());
1649 AssertComRC (rc);
1650 }
1651}
1652
1653/**
1654 * Called by USB proxy service when the state of the host-driven device
1655 * has changed because of non proxy interaction.
1656 *
1657 * @param aDevice The device in question.
1658 */
1659void Host::onUSBDeviceStateChanged (HostUSBDevice *aDevice)
1660{
1661 LogFlowMember (("Host::onUSBDeviceStateChanged: \n"));
1662 /// @todo (dmik) check locks
1663 AutoLock alock (this);
1664
1665 /** @todo dmik, is there anything we should do here? For instance if the device now is available? */
1666}
1667
1668/**
1669 * Checks for the presense and status of the USB Proxy Service.
1670 * Returns S_OK when the Proxy is present and OK, or E_FAIL and a
1671 * corresponding error message otherwise. Intended to be used by methods
1672 * that rely on the Proxy Service availability.
1673 *
1674 * @note Locks this object for reading.
1675 */
1676HRESULT Host::checkUSBProxyService()
1677{
1678#ifdef VBOX_WITH_USB
1679 AutoLock lock (this);
1680 CHECK_READY();
1681
1682 AssertReturn (mUSBProxyService, E_FAIL);
1683 if (!mUSBProxyService->isActive())
1684 {
1685 /* disable the USB controller completely to avoid assertions if the
1686 * USB proxy service could not start. */
1687
1688 Assert (VBOX_FAILURE (mUSBProxyService->getLastError()));
1689 if (mUSBProxyService->getLastError() == VERR_FILE_NOT_FOUND)
1690 return setError (E_FAIL,
1691 tr ("Could not load the Host USB Proxy Service (%Vrc)."
1692 "The service might be not installed on the host computer"),
1693 mUSBProxyService->getLastError());
1694 else
1695 return setError (E_FAIL,
1696 tr ("Could not load the Host USB Proxy service (%Vrc)"),
1697 mUSBProxyService->getLastError());
1698 }
1699
1700 return S_OK;
1701#else
1702 return E_NOTIMPL;
1703#endif
1704}
1705
1706#ifdef __WIN__
1707
1708/* The original source of the VBoxTAP adapter creation/destruction code has the following copyright */
1709/*
1710 Copyright 2004 by the Massachusetts Institute of Technology
1711
1712 All rights reserved.
1713
1714 Permission to use, copy, modify, and distribute this software and its
1715 documentation for any purpose and without fee is hereby granted,
1716 provided that the above copyright notice appear in all copies and that
1717 both that copyright notice and this permission notice appear in
1718 supporting documentation, and that the name of the Massachusetts
1719 Institute of Technology (M.I.T.) not be used in advertising or publicity
1720 pertaining to distribution of the software without specific, written
1721 prior permission.
1722
1723 M.I.T. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
1724 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
1725 M.I.T. BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
1726 ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
1727 WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
1728 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
1729 SOFTWARE.
1730*/
1731
1732
1733#define NETSHELL_LIBRARY _T("netshell.dll")
1734
1735/**
1736 * Use the IShellFolder API to rename the connection.
1737 */
1738static HRESULT rename_shellfolder (PCWSTR wGuid, PCWSTR wNewName)
1739{
1740 /* This is the GUID for the network connections folder. It is constant.
1741 * {7007ACC7-3202-11D1-AAD2-00805FC1270E} */
1742 const GUID CLSID_NetworkConnections = {
1743 0x7007ACC7, 0x3202, 0x11D1, {
1744 0xAA, 0xD2, 0x00, 0x80, 0x5F, 0xC1, 0x27, 0x0E
1745 }
1746 };
1747
1748 LPITEMIDLIST pidl = NULL;
1749 IShellFolder *pShellFolder = NULL;
1750 HRESULT hr;
1751
1752 /* Build the display name in the form "::{GUID}". */
1753 if (wcslen (wGuid) >= MAX_PATH)
1754 return E_INVALIDARG;
1755 WCHAR szAdapterGuid[MAX_PATH + 2] = {0};
1756 swprintf (szAdapterGuid, L"::%ls", wGuid);
1757
1758 /* Create an instance of the network connections folder. */
1759 hr = CoCreateInstance (CLSID_NetworkConnections, NULL,
1760 CLSCTX_INPROC_SERVER, IID_IShellFolder,
1761 reinterpret_cast <LPVOID *> (&pShellFolder));
1762 /* Parse the display name. */
1763 if (SUCCEEDED (hr))
1764 {
1765 hr = pShellFolder->ParseDisplayName (NULL, NULL, szAdapterGuid, NULL,
1766 &pidl, NULL);
1767 }
1768 if (SUCCEEDED (hr))
1769 {
1770 hr = pShellFolder->SetNameOf (NULL, pidl, wNewName, SHGDN_NORMAL,
1771 &pidl);
1772 }
1773
1774 CoTaskMemFree (pidl);
1775
1776 if (pShellFolder)
1777 pShellFolder->Release();
1778
1779 return hr;
1780}
1781
1782extern "C" HRESULT RenameConnection (PCWSTR GuidString, PCWSTR NewName)
1783{
1784 typedef HRESULT (WINAPI *lpHrRenameConnection) (const GUID *, PCWSTR);
1785 lpHrRenameConnection RenameConnectionFunc = NULL;
1786 HRESULT status;
1787
1788 /* First try the IShellFolder interface, which was unimplemented
1789 * for the network connections folder before XP. */
1790 status = rename_shellfolder (GuidString, NewName);
1791 if (status == E_NOTIMPL)
1792 {
1793/** @todo that code doesn't seem to work! */
1794 /* The IShellFolder interface is not implemented on this platform.
1795 * Try the (undocumented) HrRenameConnection API in the netshell
1796 * library. */
1797 CLSID clsid;
1798 HINSTANCE hNetShell;
1799 status = CLSIDFromString ((LPOLESTR) GuidString, &clsid);
1800 if (FAILED(status))
1801 return E_FAIL;
1802 hNetShell = LoadLibrary (NETSHELL_LIBRARY);
1803 if (hNetShell == NULL)
1804 return E_FAIL;
1805 RenameConnectionFunc =
1806 (lpHrRenameConnection) GetProcAddress (hNetShell,
1807 "HrRenameConnection");
1808 if (RenameConnectionFunc == NULL)
1809 {
1810 FreeLibrary (hNetShell);
1811 return E_FAIL;
1812 }
1813 status = RenameConnectionFunc (&clsid, NewName);
1814 FreeLibrary (hNetShell);
1815 }
1816 if (FAILED (status))
1817 return status;
1818
1819 return S_OK;
1820}
1821
1822#define DRIVERHWID _T("vboxtap")
1823
1824#define SetErrBreak(strAndArgs) \
1825 if (1) { \
1826 aErrMsg = Utf8StrFmt strAndArgs; vrc = VERR_GENERAL_FAILURE; break; \
1827 } else do {} while (0)
1828
1829/* static */
1830int Host::createNetworkInterface (SVCHlpClient *aClient,
1831 const Utf8Str &aName,
1832 Guid &aGUID, Utf8Str &aErrMsg)
1833{
1834 LogFlowFuncEnter();
1835 LogFlowFunc (("Network connection name = '%s'\n", aName.raw()));
1836
1837 AssertReturn (aClient, VERR_INVALID_POINTER);
1838 AssertReturn (!aName.isNull(), VERR_INVALID_PARAMETER);
1839
1840 int vrc = VINF_SUCCESS;
1841
1842 HDEVINFO hDeviceInfo = INVALID_HANDLE_VALUE;
1843 SP_DEVINFO_DATA DeviceInfoData;
1844 DWORD ret = 0;
1845 BOOL found = FALSE;
1846 BOOL registered = FALSE;
1847 BOOL destroyList = FALSE;
1848 TCHAR pCfgGuidString [50];
1849
1850 do
1851 {
1852 BOOL ok;
1853 GUID netGuid;
1854 SP_DRVINFO_DATA DriverInfoData;
1855 SP_DEVINSTALL_PARAMS DeviceInstallParams;
1856 TCHAR className [MAX_PATH];
1857 DWORD index = 0;
1858 PSP_DRVINFO_DETAIL_DATA pDriverInfoDetail;
1859 /* for our purposes, 2k buffer is more
1860 * than enough to obtain the hardware ID
1861 * of the VBoxTAP driver. */
1862 DWORD detailBuf [2048];
1863
1864 HKEY hkey = NULL;
1865 DWORD cbSize;
1866 DWORD dwValueType;
1867
1868 /* initialize the structure size */
1869 DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
1870 DriverInfoData.cbSize = sizeof(SP_DRVINFO_DATA);
1871
1872 /* copy the net class GUID */
1873 memcpy(&netGuid, &GUID_DEVCLASS_NET, sizeof(GUID_DEVCLASS_NET));
1874
1875 /* create an empty device info set associated with the net class GUID */
1876 hDeviceInfo = SetupDiCreateDeviceInfoList (&netGuid, NULL);
1877 if (hDeviceInfo == INVALID_HANDLE_VALUE)
1878 SetErrBreak (("SetupDiCreateDeviceInfoList failed (0x%08X)",
1879 GetLastError()));
1880
1881 /* get the class name from GUID */
1882 ok = SetupDiClassNameFromGuid (&netGuid, className, MAX_PATH, NULL);
1883 if (!ok)
1884 SetErrBreak (("SetupDiClassNameFromGuid failed (0x%08X)",
1885 GetLastError()));
1886
1887 /* create a device info element and add the new device instance
1888 * key to registry */
1889 ok = SetupDiCreateDeviceInfo (hDeviceInfo, className, &netGuid, NULL, NULL,
1890 DICD_GENERATE_ID, &DeviceInfoData);
1891 if (!ok)
1892 SetErrBreak (("SetupDiCreateDeviceInfo failed (0x%08X)",
1893 GetLastError()));
1894
1895 /* select the newly created device info to be the currently
1896 selected member */
1897 ok = SetupDiSetSelectedDevice (hDeviceInfo, &DeviceInfoData);
1898 if (!ok)
1899 SetErrBreak (("SetupDiSetSelectedDevice failed (0x%08X)",
1900 GetLastError()));
1901
1902 /* build a list of class drivers */
1903 ok = SetupDiBuildDriverInfoList (hDeviceInfo, &DeviceInfoData,
1904 SPDIT_CLASSDRIVER);
1905 if (!ok)
1906 SetErrBreak (("SetupDiBuildDriverInfoList failed (0x%08X)",
1907 GetLastError()));
1908
1909 destroyList = TRUE;
1910
1911 /* enumerate the driver info list */
1912 while (TRUE)
1913 {
1914 BOOL ret;
1915
1916 ret = SetupDiEnumDriverInfo (hDeviceInfo, &DeviceInfoData,
1917 SPDIT_CLASSDRIVER, index, &DriverInfoData);
1918
1919 /* if the function failed and GetLastError() returned
1920 * ERROR_NO_MORE_ITEMS, then we have reached the end of the
1921 * list. Othewise there was something wrong with this
1922 * particular driver. */
1923 if (!ret)
1924 {
1925 if(GetLastError() == ERROR_NO_MORE_ITEMS)
1926 break;
1927 else
1928 {
1929 index++;
1930 continue;
1931 }
1932 }
1933
1934 pDriverInfoDetail = (PSP_DRVINFO_DETAIL_DATA) detailBuf;
1935 pDriverInfoDetail->cbSize = sizeof(SP_DRVINFO_DETAIL_DATA);
1936
1937 /* if we successfully find the hardware ID and it turns out to
1938 * be the one for the loopback driver, then we are done. */
1939 if (SetupDiGetDriverInfoDetail (hDeviceInfo,
1940 &DeviceInfoData,
1941 &DriverInfoData,
1942 pDriverInfoDetail,
1943 sizeof (detailBuf),
1944 NULL))
1945 {
1946 TCHAR * t;
1947
1948 /* pDriverInfoDetail->HardwareID is a MULTISZ string. Go through the
1949 * whole list and see if there is a match somewhere. */
1950 t = pDriverInfoDetail->HardwareID;
1951 while (t && *t && t < (TCHAR *) &detailBuf [sizeof(detailBuf) / sizeof (detailBuf[0])])
1952 {
1953 if (!_tcsicmp(t, DRIVERHWID))
1954 break;
1955
1956 t += _tcslen(t) + 1;
1957 }
1958
1959 if (t && *t && t < (TCHAR *) &detailBuf [sizeof(detailBuf) / sizeof (detailBuf[0])])
1960 {
1961 found = TRUE;
1962 break;
1963 }
1964 }
1965
1966 index ++;
1967 }
1968
1969 if (!found)
1970 SetErrBreak ((tr ("Could not find Host Interface Networking driver! "
1971 "Please reinstall")));
1972
1973 /* set the loopback driver to be the currently selected */
1974 ok = SetupDiSetSelectedDriver (hDeviceInfo, &DeviceInfoData,
1975 &DriverInfoData);
1976 if (!ok)
1977 SetErrBreak (("SetupDiSetSelectedDriver failed (0x%08X)",
1978 GetLastError()));
1979
1980 /* register the phantom device to prepare for install */
1981 ok = SetupDiCallClassInstaller (DIF_REGISTERDEVICE, hDeviceInfo,
1982 &DeviceInfoData);
1983 if (!ok)
1984 SetErrBreak (("SetupDiCallClassInstaller failed (0x%08X)",
1985 GetLastError()));
1986
1987 /* registered, but remove if errors occur in the following code */
1988 registered = TRUE;
1989
1990 /* ask the installer if we can install the device */
1991 ok = SetupDiCallClassInstaller (DIF_ALLOW_INSTALL, hDeviceInfo,
1992 &DeviceInfoData);
1993 if (!ok)
1994 {
1995 if (GetLastError() != ERROR_DI_DO_DEFAULT)
1996 SetErrBreak (("SetupDiCallClassInstaller (DIF_ALLOW_INSTALL) failed (0x%08X)",
1997 GetLastError()));
1998 /* that's fine */
1999 }
2000
2001 /* install the files first */
2002 ok = SetupDiCallClassInstaller (DIF_INSTALLDEVICEFILES, hDeviceInfo,
2003 &DeviceInfoData);
2004 if (!ok)
2005 SetErrBreak (("SetupDiCallClassInstaller (DIF_INSTALLDEVICEFILES) failed (0x%08X)",
2006 GetLastError()));
2007
2008 /* get the device install parameters and disable filecopy */
2009 DeviceInstallParams.cbSize = sizeof(SP_DEVINSTALL_PARAMS);
2010 ok = SetupDiGetDeviceInstallParams (hDeviceInfo, &DeviceInfoData,
2011 &DeviceInstallParams);
2012 if (ok)
2013 {
2014 DeviceInstallParams.Flags |= DI_NOFILECOPY;
2015 ok = SetupDiSetDeviceInstallParams (hDeviceInfo, &DeviceInfoData,
2016 &DeviceInstallParams);
2017 if (!ok)
2018 SetErrBreak (("SetupDiSetDeviceInstallParams failed (0x%08X)",
2019 GetLastError()));
2020 }
2021
2022 /*
2023 * Register any device-specific co-installers for this device,
2024 */
2025
2026 ok = SetupDiCallClassInstaller (DIF_REGISTER_COINSTALLERS,
2027 hDeviceInfo,
2028 &DeviceInfoData);
2029 if (!ok)
2030 SetErrBreak (("SetupDiCallClassInstaller (DIF_REGISTER_COINSTALLERS) failed (0x%08X)",
2031 GetLastError()));
2032
2033 /*
2034 * install any installer-specified interfaces.
2035 * and then do the real install
2036 */
2037 ok = SetupDiCallClassInstaller (DIF_INSTALLINTERFACES,
2038 hDeviceInfo,
2039 &DeviceInfoData);
2040 if (!ok)
2041 SetErrBreak (("SetupDiCallClassInstaller (DIF_INSTALLINTERFACES) failed (0x%08X)",
2042 GetLastError()));
2043
2044 ok = SetupDiCallClassInstaller (DIF_INSTALLDEVICE,
2045 hDeviceInfo,
2046 &DeviceInfoData);
2047 if (!ok)
2048 SetErrBreak (("SetupDiCallClassInstaller (DIF_INSTALLDEVICE) failed (0x%08X)",
2049 GetLastError()));
2050
2051 /* Figure out NetCfgInstanceId */
2052 hkey = SetupDiOpenDevRegKey (hDeviceInfo,
2053 &DeviceInfoData,
2054 DICS_FLAG_GLOBAL,
2055 0,
2056 DIREG_DRV,
2057 KEY_READ);
2058 if (hkey == INVALID_HANDLE_VALUE)
2059 SetErrBreak (("SetupDiOpenDevRegKey failed (0x%08X)",
2060 GetLastError()));
2061
2062 cbSize = sizeof (pCfgGuidString);
2063 DWORD ret;
2064 ret = RegQueryValueEx (hkey, _T ("NetCfgInstanceId"), NULL,
2065 &dwValueType, (LPBYTE) pCfgGuidString, &cbSize);
2066 RegCloseKey (hkey);
2067
2068 ret = RenameConnection (pCfgGuidString, Bstr (aName));
2069 if (FAILED (ret))
2070 SetErrBreak (("Failed to set interface name (ret=0x%08X, "
2071 "pCfgGuidString='%ls', cbSize=%d)",
2072 ret, pCfgGuidString, cbSize));
2073 }
2074 while (0);
2075
2076 /*
2077 * cleanup
2078 */
2079
2080 if (hDeviceInfo != INVALID_HANDLE_VALUE)
2081 {
2082 /* an error has occured, but the device is registered, we must remove it */
2083 if (ret != 0 && registered)
2084 SetupDiCallClassInstaller (DIF_REMOVE, hDeviceInfo, &DeviceInfoData);
2085
2086 found = SetupDiDeleteDeviceInfo (hDeviceInfo, &DeviceInfoData);
2087
2088 /* destroy the driver info list */
2089 if (destroyList)
2090 SetupDiDestroyDriverInfoList (hDeviceInfo, &DeviceInfoData,
2091 SPDIT_CLASSDRIVER);
2092 /* clean up the device info set */
2093 SetupDiDestroyDeviceInfoList (hDeviceInfo);
2094 }
2095
2096 /* return the network connection GUID on success */
2097 if (VBOX_SUCCESS (vrc))
2098 {
2099 /* remove the curly bracket at the end */
2100 pCfgGuidString [_tcslen (pCfgGuidString) - 1] = '\0';
2101 LogFlowFunc (("Network connection GUID string = {%ls}\n", pCfgGuidString + 1));
2102
2103 aGUID = Guid (Utf8Str (pCfgGuidString + 1));
2104 LogFlowFunc (("Network connection GUID = {%Vuuid}\n", aGUID.raw()));
2105 Assert (!aGUID.isEmpty());
2106 }
2107
2108 LogFlowFunc (("vrc=%Vrc\n", vrc));
2109 LogFlowFuncLeave();
2110 return vrc;
2111}
2112
2113/* static */
2114int Host::removeNetworkInterface (SVCHlpClient *aClient,
2115 const Guid &aGUID,
2116 Utf8Str &aErrMsg)
2117{
2118 LogFlowFuncEnter();
2119 LogFlowFunc (("Network connection GUID = {%Vuuid}\n", aGUID.raw()));
2120
2121 AssertReturn (aClient, VERR_INVALID_POINTER);
2122 AssertReturn (!aGUID.isEmpty(), VERR_INVALID_PARAMETER);
2123
2124 int vrc = VINF_SUCCESS;
2125
2126 do
2127 {
2128 TCHAR lszPnPInstanceId [512] = {0};
2129
2130 /* We have to find the device instance ID through a registry search */
2131
2132 HKEY hkeyNetwork = 0;
2133 HKEY hkeyConnection = 0;
2134
2135 do
2136 {
2137 char strRegLocation [256];
2138 sprintf (strRegLocation,
2139 "SYSTEM\\CurrentControlSet\\Control\\Network\\"
2140 "{4D36E972-E325-11CE-BFC1-08002BE10318}\\{%s}",
2141 aGUID.toString().raw());
2142 LONG status;
2143 status = RegOpenKeyExA (HKEY_LOCAL_MACHINE, strRegLocation, 0,
2144 KEY_READ, &hkeyNetwork);
2145 if ((status != ERROR_SUCCESS) || !hkeyNetwork)
2146 SetErrBreak ((
2147 tr ("Host interface network is not found in registry (%s) [1]"),
2148 strRegLocation));
2149
2150 status = RegOpenKeyExA (hkeyNetwork, "Connection", 0,
2151 KEY_READ, &hkeyConnection);
2152 if ((status != ERROR_SUCCESS) || !hkeyConnection)
2153 SetErrBreak ((
2154 tr ("Host interface network is not found in registry (%s) [2]"),
2155 strRegLocation));
2156
2157 DWORD len = sizeof (lszPnPInstanceId);
2158 DWORD dwKeyType;
2159 status = RegQueryValueExW (hkeyConnection, L"PnPInstanceID", NULL,
2160 &dwKeyType, (LPBYTE) lszPnPInstanceId, &len);
2161 if ((status != ERROR_SUCCESS) || (dwKeyType != REG_SZ))
2162 SetErrBreak ((
2163 tr ("Host interface network is not found in registry (%s) [3]"),
2164 strRegLocation));
2165 }
2166 while (0);
2167
2168 if (hkeyConnection)
2169 RegCloseKey (hkeyConnection);
2170 if (hkeyNetwork)
2171 RegCloseKey (hkeyNetwork);
2172
2173 if (VBOX_FAILURE (vrc))
2174 break;
2175
2176 /*
2177 * Now we are going to enumerate all network devices and
2178 * wait until we encounter the right device instance ID
2179 */
2180
2181 HDEVINFO hDeviceInfo = INVALID_HANDLE_VALUE;
2182
2183 do
2184 {
2185 BOOL ok;
2186 DWORD ret = 0;
2187 GUID netGuid;
2188 SP_DEVINFO_DATA DeviceInfoData;
2189 DWORD index = 0;
2190 BOOL found = FALSE;
2191 DWORD size = 0;
2192
2193 /* initialize the structure size */
2194 DeviceInfoData.cbSize = sizeof (SP_DEVINFO_DATA);
2195
2196 /* copy the net class GUID */
2197 memcpy (&netGuid, &GUID_DEVCLASS_NET, sizeof (GUID_DEVCLASS_NET));
2198
2199 /* return a device info set contains all installed devices of the Net class */
2200 hDeviceInfo = SetupDiGetClassDevs (&netGuid, NULL, NULL, DIGCF_PRESENT);
2201
2202 if (hDeviceInfo == INVALID_HANDLE_VALUE)
2203 SetErrBreak (("SetupDiGetClassDevs failed (0x%08X)", GetLastError()));
2204
2205 /* enumerate the driver info list */
2206 while (TRUE)
2207 {
2208 TCHAR *deviceHwid;
2209
2210 ok = SetupDiEnumDeviceInfo (hDeviceInfo, index, &DeviceInfoData);
2211
2212 if (!ok)
2213 {
2214 if (GetLastError() == ERROR_NO_MORE_ITEMS)
2215 break;
2216 else
2217 {
2218 index++;
2219 continue;
2220 }
2221 }
2222
2223 /* try to get the hardware ID registry property */
2224 ok = SetupDiGetDeviceRegistryProperty (hDeviceInfo,
2225 &DeviceInfoData,
2226 SPDRP_HARDWAREID,
2227 NULL,
2228 NULL,
2229 0,
2230 &size);
2231 if (!ok)
2232 {
2233 if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
2234 {
2235 index++;
2236 continue;
2237 }
2238
2239 deviceHwid = (TCHAR *) malloc (size);
2240 ok = SetupDiGetDeviceRegistryProperty (hDeviceInfo,
2241 &DeviceInfoData,
2242 SPDRP_HARDWAREID,
2243 NULL,
2244 (PBYTE)deviceHwid,
2245 size,
2246 NULL);
2247 if (!ok)
2248 {
2249 free (deviceHwid);
2250 deviceHwid = NULL;
2251 index++;
2252 continue;
2253 }
2254 }
2255 else
2256 {
2257 /* something is wrong. This shouldn't have worked with a NULL buffer */
2258 index++;
2259 continue;
2260 }
2261
2262 for (TCHAR *t = deviceHwid;
2263 t && *t && t < &deviceHwid[size / sizeof(TCHAR)];
2264 t += _tcslen (t) + 1)
2265 {
2266 if (!_tcsicmp (DRIVERHWID, t))
2267 {
2268 /* get the device instance ID */
2269 TCHAR devID [MAX_DEVICE_ID_LEN];
2270 if (CM_Get_Device_ID(DeviceInfoData.DevInst,
2271 devID, MAX_DEVICE_ID_LEN, 0) == CR_SUCCESS)
2272 {
2273 /* compare to what we determined before */
2274 if (wcscmp(devID, lszPnPInstanceId) == 0)
2275 {
2276 found = TRUE;
2277 break;
2278 }
2279 }
2280 }
2281 }
2282
2283 if (deviceHwid)
2284 {
2285 free (deviceHwid);
2286 deviceHwid = NULL;
2287 }
2288
2289 if (found)
2290 break;
2291
2292 index++;
2293 }
2294
2295 if (found == FALSE)
2296 SetErrBreak ((tr ("Host Interface Network driver not found (0x%08X)"),
2297 GetLastError()));
2298
2299 ok = SetupDiSetSelectedDevice (hDeviceInfo, &DeviceInfoData);
2300 if (!ok)
2301 SetErrBreak (("SetupDiSetSelectedDevice failed (0x%08X)",
2302 GetLastError()));
2303
2304 ok = SetupDiCallClassInstaller (DIF_REMOVE, hDeviceInfo, &DeviceInfoData);
2305 if (!ok)
2306 SetErrBreak (("SetupDiCallClassInstaller (DIF_REMOVE) failed (0x%08X)",
2307 GetLastError()));
2308 }
2309 while (0);
2310
2311 /* clean up the device info set */
2312 if (hDeviceInfo != INVALID_HANDLE_VALUE)
2313 SetupDiDestroyDeviceInfoList (hDeviceInfo);
2314
2315 if (VBOX_FAILURE (vrc))
2316 break;
2317 }
2318 while (0);
2319
2320 LogFlowFunc (("vrc=%Vrc\n", vrc));
2321 LogFlowFuncLeave();
2322 return vrc;
2323}
2324
2325#undef SetErrBreak
2326
2327/* static */
2328HRESULT Host::networkInterfaceHelperClient (SVCHlpClient *aClient,
2329 Progress *aProgress,
2330 void *aUser, int *aVrc)
2331{
2332 LogFlowFuncEnter();
2333 LogFlowFunc (("aClient={%p}, aProgress={%p}, aUser={%p}\n",
2334 aClient, aProgress, aUser));
2335
2336 AssertReturn ((aClient == NULL && aProgress == NULL && aVrc == NULL) ||
2337 (aClient != NULL && aProgress != NULL && aVrc != NULL),
2338 E_POINTER);
2339 AssertReturn (aUser, E_POINTER);
2340
2341 std::auto_ptr <NetworkInterfaceHelperClientData>
2342 d (static_cast <NetworkInterfaceHelperClientData *> (aUser));
2343
2344 if (aClient == NULL)
2345 {
2346 /* "cleanup only" mode, just return (it will free aUser) */
2347 return S_OK;
2348 }
2349
2350 HRESULT rc = S_OK;
2351 int vrc = VINF_SUCCESS;
2352
2353 switch (d->msgCode)
2354 {
2355 case SVCHlpMsg::CreateHostNetworkInterface:
2356 {
2357 LogFlowFunc (("CreateHostNetworkInterface:\n"));
2358 LogFlowFunc (("Network connection name = '%ls'\n", d->name.raw()));
2359
2360 /* write message and parameters */
2361 vrc = aClient->write (d->msgCode);
2362 if (VBOX_FAILURE (vrc)) break;
2363 vrc = aClient->write (Utf8Str (d->name));
2364 if (VBOX_FAILURE (vrc)) break;
2365
2366 /* wait for a reply */
2367 bool endLoop = false;
2368 while (!endLoop)
2369 {
2370 SVCHlpMsg::Code reply = SVCHlpMsg::Null;
2371
2372 vrc = aClient->read (reply);
2373 if (VBOX_FAILURE (vrc)) break;
2374
2375 switch (reply)
2376 {
2377 case SVCHlpMsg::CreateHostNetworkInterface_OK:
2378 {
2379 /* read the GUID */
2380 Guid guid;
2381 vrc = aClient->read (guid);
2382 if (VBOX_FAILURE (vrc)) break;
2383
2384 LogFlowFunc (("Network connection GUID = {%Vuuid}\n", guid.raw()));
2385
2386 /* initialize the object returned to the caller by
2387 * CreateHostNetworkInterface() */
2388 rc = d->iface->init (d->name, guid);
2389 endLoop = true;
2390 break;
2391 }
2392 case SVCHlpMsg::Error:
2393 {
2394 /* read the error message */
2395 Utf8Str errMsg;
2396 vrc = aClient->read (errMsg);
2397 if (VBOX_FAILURE (vrc)) break;
2398
2399 rc = setError (E_FAIL, errMsg);
2400 endLoop = true;
2401 break;
2402 }
2403 default:
2404 {
2405 endLoop = true;
2406 ComAssertMsgFailedBreak ((
2407 "Invalid message code %d (%08lX)\n",
2408 reply, reply),
2409 rc = E_FAIL);
2410 }
2411 }
2412 }
2413
2414 break;
2415 }
2416 case SVCHlpMsg::RemoveHostNetworkInterface:
2417 {
2418 LogFlowFunc (("RemoveHostNetworkInterface:\n"));
2419 LogFlowFunc (("Network connection GUID = {%Vuuid}\n", d->guid.raw()));
2420
2421 /* write message and parameters */
2422 vrc = aClient->write (d->msgCode);
2423 if (VBOX_FAILURE (vrc)) break;
2424 vrc = aClient->write (d->guid);
2425 if (VBOX_FAILURE (vrc)) break;
2426
2427 /* wait for a reply */
2428 bool endLoop = false;
2429 while (!endLoop)
2430 {
2431 SVCHlpMsg::Code reply = SVCHlpMsg::Null;
2432
2433 vrc = aClient->read (reply);
2434 if (VBOX_FAILURE (vrc)) break;
2435
2436 switch (reply)
2437 {
2438 case SVCHlpMsg::OK:
2439 {
2440 /* no parameters */
2441 rc = S_OK;
2442 endLoop = true;
2443 break;
2444 }
2445 case SVCHlpMsg::Error:
2446 {
2447 /* read the error message */
2448 Utf8Str errMsg;
2449 vrc = aClient->read (errMsg);
2450 if (VBOX_FAILURE (vrc)) break;
2451
2452 rc = setError (E_FAIL, errMsg);
2453 endLoop = true;
2454 break;
2455 }
2456 default:
2457 {
2458 endLoop = true;
2459 ComAssertMsgFailedBreak ((
2460 "Invalid message code %d (%08lX)\n",
2461 reply, reply),
2462 rc = E_FAIL);
2463 }
2464 }
2465 }
2466
2467 break;
2468 }
2469 default:
2470 ComAssertMsgFailedBreak ((
2471 "Invalid message code %d (%08lX)\n",
2472 d->msgCode, d->msgCode),
2473 rc = E_FAIL);
2474 }
2475
2476 if (aVrc)
2477 *aVrc = vrc;
2478
2479 LogFlowFunc (("rc=0x%08X, vrc=%Vrc\n", rc, vrc));
2480 LogFlowFuncLeave();
2481 return rc;
2482}
2483
2484/* static */
2485int Host::networkInterfaceHelperServer (SVCHlpClient *aClient,
2486 SVCHlpMsg::Code aMsgCode)
2487{
2488 LogFlowFuncEnter();
2489 LogFlowFunc (("aClient={%p}, aMsgCode=%d\n", aClient, aMsgCode));
2490
2491 AssertReturn (aClient, VERR_INVALID_POINTER);
2492
2493 int vrc = VINF_SUCCESS;
2494
2495 switch (aMsgCode)
2496 {
2497 case SVCHlpMsg::CreateHostNetworkInterface:
2498 {
2499 LogFlowFunc (("CreateHostNetworkInterface:\n"));
2500
2501 Utf8Str name;
2502 vrc = aClient->read (name);
2503 if (VBOX_FAILURE (vrc)) break;
2504
2505 Guid guid;
2506 Utf8Str errMsg;
2507 vrc = createNetworkInterface (aClient, name, guid, errMsg);
2508
2509 if (VBOX_SUCCESS (vrc))
2510 {
2511 /* write success followed by GUID */
2512 vrc = aClient->write (SVCHlpMsg::CreateHostNetworkInterface_OK);
2513 if (VBOX_FAILURE (vrc)) break;
2514 vrc = aClient->write (guid);
2515 if (VBOX_FAILURE (vrc)) break;
2516 }
2517 else
2518 {
2519 /* write failure followed by error message */
2520 if (errMsg.isEmpty())
2521 errMsg = Utf8StrFmt ("Unspecified error (%Vrc)", vrc);
2522 vrc = aClient->write (SVCHlpMsg::Error);
2523 if (VBOX_FAILURE (vrc)) break;
2524 vrc = aClient->write (errMsg);
2525 if (VBOX_FAILURE (vrc)) break;
2526 }
2527
2528 break;
2529 }
2530 case SVCHlpMsg::RemoveHostNetworkInterface:
2531 {
2532 LogFlowFunc (("RemoveHostNetworkInterface:\n"));
2533
2534 Guid guid;
2535 vrc = aClient->read (guid);
2536 if (VBOX_FAILURE (vrc)) break;
2537
2538 Utf8Str errMsg;
2539 vrc = removeNetworkInterface (aClient, guid, errMsg);
2540
2541 if (VBOX_SUCCESS (vrc))
2542 {
2543 /* write parameter-less success */
2544 vrc = aClient->write (SVCHlpMsg::OK);
2545 if (VBOX_FAILURE (vrc)) break;
2546 }
2547 else
2548 {
2549 /* write failure followed by error message */
2550 if (errMsg.isEmpty())
2551 errMsg = Utf8StrFmt ("Unspecified error (%Vrc)", vrc);
2552 vrc = aClient->write (SVCHlpMsg::Error);
2553 if (VBOX_FAILURE (vrc)) break;
2554 vrc = aClient->write (errMsg);
2555 if (VBOX_FAILURE (vrc)) break;
2556 }
2557
2558 break;
2559 }
2560 default:
2561 AssertMsgFailedBreak ((
2562 "Invalid message code %d (%08lX)\n", aMsgCode, aMsgCode),
2563 VERR_GENERAL_FAILURE);
2564 }
2565
2566 LogFlowFunc (("vrc=%Vrc\n", vrc));
2567 LogFlowFuncLeave();
2568 return vrc;
2569}
2570
2571#endif /* __WIN__ */
2572
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette