VirtualBox

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

Last change on this file since 1830 was 1762, checked in by vboxsync, 18 years ago

Fixed uninitialized input variable in CDROM media presence check.

  • 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 cdChannelInfo.cdsc_format = CDROM_MSF;
1444 // this call will finally reveal the whole truth
1445 if ((ioctl(fileHandle, CDROMSUBCHNL, &cdChannelInfo) == 0) ||
1446 (errno == EIO) || (errno == ENOENT) ||
1447 (errno == EINVAL) || (errno == ENOMEDIUM))
1448 {
1449 retValue = true;
1450 }
1451 close(fileHandle);
1452 }
1453 }
1454 } else
1455 {
1456 // floppy case
1457 if (S_ISCHR(statInfo.st_mode) || S_ISBLK(statInfo.st_mode))
1458 {
1459 /// @todo do some more testing, maybe a nice IOCTL!
1460 retValue = true;
1461 }
1462 }
1463 }
1464 return retValue;
1465}
1466#endif // __LINUX__
1467
1468/**
1469 * Runs through all global filters to determine the state of the given
1470 * USB device, then unless ignored, runs trhough filters of all running machines
1471 * (excluding the given one) to automatically capture the device when there is a
1472 * match (the state of the device will be set to USBDeviceCaptured if so, and
1473 * the machine's process will be informed about the auto-capture).
1474 *
1475 * @param aDevice USB device to set state for
1476 * @param aMachine the machine whose filters are to be excluded (can be NULL)
1477 *
1478 * @note the method must be called from under this object's lock
1479 */
1480HRESULT Host::applyAllUSBFilters (ComObjPtr <HostUSBDevice> &aDevice,
1481 SessionMachine *aMachine /* = NULL */)
1482{
1483 LogFlowMember (("Host::applyAllUSBFilters: \n"));
1484
1485 // ignore unsupported devices
1486 if (aDevice->state() == USBDeviceState_USBDeviceNotSupported)
1487 return S_OK;
1488 // ignore unavailable devices as well
1489 if (aDevice->state() == USBDeviceState_USBDeviceUnavailable)
1490 return S_OK;
1491
1492 VirtualBox::SessionMachineVector machines;
1493 mParent->getOpenedMachines (machines);
1494
1495 // apply global filters
1496 USBDeviceFilterList::const_iterator it = mUSBDeviceFilters.begin();
1497 while (it != mUSBDeviceFilters.end())
1498 {
1499 AutoLock filterLock (*it);
1500 const HostUSBDeviceFilter::Data &data = (*it)->data();
1501 if (aDevice->isMatch (data))
1502 {
1503 if (data.mAction == USBDeviceFilterAction_USBDeviceFilterIgnore)
1504 return S_OK;
1505 if (data.mAction == USBDeviceFilterAction_USBDeviceFilterHold)
1506 break;
1507 }
1508 ++ it;
1509 }
1510
1511 // apply machine filters
1512 for (size_t i = 0; i < machines.size(); i++)
1513 {
1514 if (aMachine && machines [i] == aMachine)
1515 continue;
1516
1517 applyMachineUSBFilters (machines [i], aDevice);
1518
1519 if (aDevice->state() == USBDeviceState_USBDeviceCaptured)
1520 {
1521 // inform the machine's process about the auto-capture
1522 ComPtr <IUSBDevice> d;
1523 aDevice.queryInterfaceTo (d.asOutParam());
1524 HRESULT rc = machines [i]->onUSBDeviceAttach (d);
1525 if (SUCCEEDED(rc))
1526 return rc;
1527
1528 // the machine rejected it, continue applying filters.
1529 aDevice->reset();
1530 }
1531 }
1532
1533 // no machine filters were matched.
1534 // if no global filters were matched as well, release the device
1535 // to make it available on the host
1536 if ( it == mUSBDeviceFilters.end()
1537 && aDevice->state() == USBDeviceState_USBDeviceHeld)
1538 aDevice->setHostDriven();
1539/** @todo dmik, what about USBDeviceFilterHold??? */
1540/**
1541 * bird, everything's ok from what I see. if USBDeviceFilterHold filter was
1542 * matched, setHostDevice() will not (and should not) be called
1543 * (because it != mUSBDeviceFilters.end() in that case).
1544 */
1545
1546 return S_OK;
1547}
1548
1549/**
1550 * Runs through filters of the given machine in order to automatically capture
1551 * the given USB device when there is a match (the state of the device will
1552 * be set to USBDeviceCaptured if so, but the machine's process will *NOT* be)
1553 * informed about the auto-capture).
1554 *
1555 * @param aMachine the machine whose filters are to be run
1556 * @param aDevice USB device, a candidate for auto-attaching
1557 *
1558 * @note Locks aMachine for reading.
1559 */
1560void Host::applyMachineUSBFilters (SessionMachine *aMachine,
1561 ComObjPtr <HostUSBDevice> &aDevice)
1562{
1563 LogFlowThisFunc (("\n"));
1564
1565 AssertReturn (aMachine, (void) 0);
1566 AssertReturn (aDevice->state() != USBDeviceState_USBDeviceUnavailable, (void) 0);
1567
1568 /* We're going to use aMachine which is not our child/parent, add a caller */
1569 AutoCaller autoCaller (aMachine);
1570 if (!autoCaller.isOk())
1571 {
1572 /* silently return, the machine might be not running any more */
1573 return;
1574 }
1575
1576 /* enter the machine's lock because we want to access its USB controller */
1577 AutoReaderLock machineLock (aMachine);
1578
1579 if (aMachine->usbController()->hasMatchingFilter (aDevice))
1580 {
1581 /* try capture the device */
1582 bool ok = aDevice->setCaptured (aMachine);
1583
1584 /*
1585 * false is valid only when the state remains USBDeviceBusy meaning that
1586 * the device is currently busy (being accessed by the host)
1587 */
1588 Assert (ok || aDevice->state() == USBDeviceState_USBDeviceBusy);
1589 NOREF (ok);
1590 }
1591}
1592
1593/**
1594 * Called by USB proxy service when a new device is physically attached
1595 * to the host.
1596 *
1597 * @param aDevice Pointer to the device which has been attached.
1598 */
1599void Host::onUSBDeviceAttached (HostUSBDevice *aDevice)
1600{
1601 LogFlowMember (("Host::onUSBDeviceAttached: aDevice=%p\n", aDevice));
1602 /// @todo (dmik) check locks
1603 AutoLock alock (this);
1604
1605 // add to the collecion
1606 mUSBDevices.push_back (aDevice);
1607
1608 // apply all filters (no need to lock the device, nobody can access it yet)
1609 ComObjPtr <HostUSBDevice> DevPtr(aDevice);
1610 HRESULT rc = applyAllUSBFilters (DevPtr);
1611 AssertComRC (rc);
1612}
1613
1614/**
1615 * Called by USB proxy service (?) when the device is physically detached
1616 * from the host.
1617 *
1618 * @param aDevice Pointer to the device which has been detached.
1619 */
1620void Host::onUSBDeviceDetached (HostUSBDevice *aDevice)
1621{
1622 LogFlowMember (("Host::onUSBDeviceDetached: aDevice=%p\n", aDevice));
1623 /// @todo (dmik) check locks
1624 AutoLock alock (this);
1625
1626 Guid id = aDevice->id();
1627
1628 ComObjPtr <HostUSBDevice> device;
1629 Host::USBDeviceList::iterator it = mUSBDevices.begin();
1630 while (it != mUSBDevices.end())
1631 {
1632 if ((*it)->id() == id)
1633 {
1634 device = (*it);
1635 break;
1636 }
1637 ++ it;
1638 }
1639
1640 AssertReturn (!!device, (void) 0);
1641
1642 // remove from the collecion
1643 mUSBDevices.erase (it);
1644
1645 if (device->machine())
1646 {
1647 // the device is captured by a machine, instruct it to release
1648 alock.unlock();
1649 HRESULT rc = device->machine()->onUSBDeviceDetach (device->id());
1650 AssertComRC (rc);
1651 }
1652}
1653
1654/**
1655 * Called by USB proxy service when the state of the host-driven device
1656 * has changed because of non proxy interaction.
1657 *
1658 * @param aDevice The device in question.
1659 */
1660void Host::onUSBDeviceStateChanged (HostUSBDevice *aDevice)
1661{
1662 LogFlowMember (("Host::onUSBDeviceStateChanged: \n"));
1663 /// @todo (dmik) check locks
1664 AutoLock alock (this);
1665
1666 /** @todo dmik, is there anything we should do here? For instance if the device now is available? */
1667}
1668
1669/**
1670 * Checks for the presense and status of the USB Proxy Service.
1671 * Returns S_OK when the Proxy is present and OK, or E_FAIL and a
1672 * corresponding error message otherwise. Intended to be used by methods
1673 * that rely on the Proxy Service availability.
1674 *
1675 * @note Locks this object for reading.
1676 */
1677HRESULT Host::checkUSBProxyService()
1678{
1679#ifdef VBOX_WITH_USB
1680 AutoLock lock (this);
1681 CHECK_READY();
1682
1683 AssertReturn (mUSBProxyService, E_FAIL);
1684 if (!mUSBProxyService->isActive())
1685 {
1686 /* disable the USB controller completely to avoid assertions if the
1687 * USB proxy service could not start. */
1688
1689 Assert (VBOX_FAILURE (mUSBProxyService->getLastError()));
1690 if (mUSBProxyService->getLastError() == VERR_FILE_NOT_FOUND)
1691 return setError (E_FAIL,
1692 tr ("Could not load the Host USB Proxy Service (%Vrc)."
1693 "The service might be not installed on the host computer"),
1694 mUSBProxyService->getLastError());
1695 else
1696 return setError (E_FAIL,
1697 tr ("Could not load the Host USB Proxy service (%Vrc)"),
1698 mUSBProxyService->getLastError());
1699 }
1700
1701 return S_OK;
1702#else
1703 return E_NOTIMPL;
1704#endif
1705}
1706
1707#ifdef __WIN__
1708
1709/* The original source of the VBoxTAP adapter creation/destruction code has the following copyright */
1710/*
1711 Copyright 2004 by the Massachusetts Institute of Technology
1712
1713 All rights reserved.
1714
1715 Permission to use, copy, modify, and distribute this software and its
1716 documentation for any purpose and without fee is hereby granted,
1717 provided that the above copyright notice appear in all copies and that
1718 both that copyright notice and this permission notice appear in
1719 supporting documentation, and that the name of the Massachusetts
1720 Institute of Technology (M.I.T.) not be used in advertising or publicity
1721 pertaining to distribution of the software without specific, written
1722 prior permission.
1723
1724 M.I.T. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
1725 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
1726 M.I.T. BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
1727 ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
1728 WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
1729 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
1730 SOFTWARE.
1731*/
1732
1733
1734#define NETSHELL_LIBRARY _T("netshell.dll")
1735
1736/**
1737 * Use the IShellFolder API to rename the connection.
1738 */
1739static HRESULT rename_shellfolder (PCWSTR wGuid, PCWSTR wNewName)
1740{
1741 /* This is the GUID for the network connections folder. It is constant.
1742 * {7007ACC7-3202-11D1-AAD2-00805FC1270E} */
1743 const GUID CLSID_NetworkConnections = {
1744 0x7007ACC7, 0x3202, 0x11D1, {
1745 0xAA, 0xD2, 0x00, 0x80, 0x5F, 0xC1, 0x27, 0x0E
1746 }
1747 };
1748
1749 LPITEMIDLIST pidl = NULL;
1750 IShellFolder *pShellFolder = NULL;
1751 HRESULT hr;
1752
1753 /* Build the display name in the form "::{GUID}". */
1754 if (wcslen (wGuid) >= MAX_PATH)
1755 return E_INVALIDARG;
1756 WCHAR szAdapterGuid[MAX_PATH + 2] = {0};
1757 swprintf (szAdapterGuid, L"::%ls", wGuid);
1758
1759 /* Create an instance of the network connections folder. */
1760 hr = CoCreateInstance (CLSID_NetworkConnections, NULL,
1761 CLSCTX_INPROC_SERVER, IID_IShellFolder,
1762 reinterpret_cast <LPVOID *> (&pShellFolder));
1763 /* Parse the display name. */
1764 if (SUCCEEDED (hr))
1765 {
1766 hr = pShellFolder->ParseDisplayName (NULL, NULL, szAdapterGuid, NULL,
1767 &pidl, NULL);
1768 }
1769 if (SUCCEEDED (hr))
1770 {
1771 hr = pShellFolder->SetNameOf (NULL, pidl, wNewName, SHGDN_NORMAL,
1772 &pidl);
1773 }
1774
1775 CoTaskMemFree (pidl);
1776
1777 if (pShellFolder)
1778 pShellFolder->Release();
1779
1780 return hr;
1781}
1782
1783extern "C" HRESULT RenameConnection (PCWSTR GuidString, PCWSTR NewName)
1784{
1785 typedef HRESULT (WINAPI *lpHrRenameConnection) (const GUID *, PCWSTR);
1786 lpHrRenameConnection RenameConnectionFunc = NULL;
1787 HRESULT status;
1788
1789 /* First try the IShellFolder interface, which was unimplemented
1790 * for the network connections folder before XP. */
1791 status = rename_shellfolder (GuidString, NewName);
1792 if (status == E_NOTIMPL)
1793 {
1794/** @todo that code doesn't seem to work! */
1795 /* The IShellFolder interface is not implemented on this platform.
1796 * Try the (undocumented) HrRenameConnection API in the netshell
1797 * library. */
1798 CLSID clsid;
1799 HINSTANCE hNetShell;
1800 status = CLSIDFromString ((LPOLESTR) GuidString, &clsid);
1801 if (FAILED(status))
1802 return E_FAIL;
1803 hNetShell = LoadLibrary (NETSHELL_LIBRARY);
1804 if (hNetShell == NULL)
1805 return E_FAIL;
1806 RenameConnectionFunc =
1807 (lpHrRenameConnection) GetProcAddress (hNetShell,
1808 "HrRenameConnection");
1809 if (RenameConnectionFunc == NULL)
1810 {
1811 FreeLibrary (hNetShell);
1812 return E_FAIL;
1813 }
1814 status = RenameConnectionFunc (&clsid, NewName);
1815 FreeLibrary (hNetShell);
1816 }
1817 if (FAILED (status))
1818 return status;
1819
1820 return S_OK;
1821}
1822
1823#define DRIVERHWID _T("vboxtap")
1824
1825#define SetErrBreak(strAndArgs) \
1826 if (1) { \
1827 aErrMsg = Utf8StrFmt strAndArgs; vrc = VERR_GENERAL_FAILURE; break; \
1828 } else do {} while (0)
1829
1830/* static */
1831int Host::createNetworkInterface (SVCHlpClient *aClient,
1832 const Utf8Str &aName,
1833 Guid &aGUID, Utf8Str &aErrMsg)
1834{
1835 LogFlowFuncEnter();
1836 LogFlowFunc (("Network connection name = '%s'\n", aName.raw()));
1837
1838 AssertReturn (aClient, VERR_INVALID_POINTER);
1839 AssertReturn (!aName.isNull(), VERR_INVALID_PARAMETER);
1840
1841 int vrc = VINF_SUCCESS;
1842
1843 HDEVINFO hDeviceInfo = INVALID_HANDLE_VALUE;
1844 SP_DEVINFO_DATA DeviceInfoData;
1845 DWORD ret = 0;
1846 BOOL found = FALSE;
1847 BOOL registered = FALSE;
1848 BOOL destroyList = FALSE;
1849 TCHAR pCfgGuidString [50];
1850
1851 do
1852 {
1853 BOOL ok;
1854 GUID netGuid;
1855 SP_DRVINFO_DATA DriverInfoData;
1856 SP_DEVINSTALL_PARAMS DeviceInstallParams;
1857 TCHAR className [MAX_PATH];
1858 DWORD index = 0;
1859 PSP_DRVINFO_DETAIL_DATA pDriverInfoDetail;
1860 /* for our purposes, 2k buffer is more
1861 * than enough to obtain the hardware ID
1862 * of the VBoxTAP driver. */
1863 DWORD detailBuf [2048];
1864
1865 HKEY hkey = NULL;
1866 DWORD cbSize;
1867 DWORD dwValueType;
1868
1869 /* initialize the structure size */
1870 DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
1871 DriverInfoData.cbSize = sizeof(SP_DRVINFO_DATA);
1872
1873 /* copy the net class GUID */
1874 memcpy(&netGuid, &GUID_DEVCLASS_NET, sizeof(GUID_DEVCLASS_NET));
1875
1876 /* create an empty device info set associated with the net class GUID */
1877 hDeviceInfo = SetupDiCreateDeviceInfoList (&netGuid, NULL);
1878 if (hDeviceInfo == INVALID_HANDLE_VALUE)
1879 SetErrBreak (("SetupDiCreateDeviceInfoList failed (0x%08X)",
1880 GetLastError()));
1881
1882 /* get the class name from GUID */
1883 ok = SetupDiClassNameFromGuid (&netGuid, className, MAX_PATH, NULL);
1884 if (!ok)
1885 SetErrBreak (("SetupDiClassNameFromGuid failed (0x%08X)",
1886 GetLastError()));
1887
1888 /* create a device info element and add the new device instance
1889 * key to registry */
1890 ok = SetupDiCreateDeviceInfo (hDeviceInfo, className, &netGuid, NULL, NULL,
1891 DICD_GENERATE_ID, &DeviceInfoData);
1892 if (!ok)
1893 SetErrBreak (("SetupDiCreateDeviceInfo failed (0x%08X)",
1894 GetLastError()));
1895
1896 /* select the newly created device info to be the currently
1897 selected member */
1898 ok = SetupDiSetSelectedDevice (hDeviceInfo, &DeviceInfoData);
1899 if (!ok)
1900 SetErrBreak (("SetupDiSetSelectedDevice failed (0x%08X)",
1901 GetLastError()));
1902
1903 /* build a list of class drivers */
1904 ok = SetupDiBuildDriverInfoList (hDeviceInfo, &DeviceInfoData,
1905 SPDIT_CLASSDRIVER);
1906 if (!ok)
1907 SetErrBreak (("SetupDiBuildDriverInfoList failed (0x%08X)",
1908 GetLastError()));
1909
1910 destroyList = TRUE;
1911
1912 /* enumerate the driver info list */
1913 while (TRUE)
1914 {
1915 BOOL ret;
1916
1917 ret = SetupDiEnumDriverInfo (hDeviceInfo, &DeviceInfoData,
1918 SPDIT_CLASSDRIVER, index, &DriverInfoData);
1919
1920 /* if the function failed and GetLastError() returned
1921 * ERROR_NO_MORE_ITEMS, then we have reached the end of the
1922 * list. Othewise there was something wrong with this
1923 * particular driver. */
1924 if (!ret)
1925 {
1926 if(GetLastError() == ERROR_NO_MORE_ITEMS)
1927 break;
1928 else
1929 {
1930 index++;
1931 continue;
1932 }
1933 }
1934
1935 pDriverInfoDetail = (PSP_DRVINFO_DETAIL_DATA) detailBuf;
1936 pDriverInfoDetail->cbSize = sizeof(SP_DRVINFO_DETAIL_DATA);
1937
1938 /* if we successfully find the hardware ID and it turns out to
1939 * be the one for the loopback driver, then we are done. */
1940 if (SetupDiGetDriverInfoDetail (hDeviceInfo,
1941 &DeviceInfoData,
1942 &DriverInfoData,
1943 pDriverInfoDetail,
1944 sizeof (detailBuf),
1945 NULL))
1946 {
1947 TCHAR * t;
1948
1949 /* pDriverInfoDetail->HardwareID is a MULTISZ string. Go through the
1950 * whole list and see if there is a match somewhere. */
1951 t = pDriverInfoDetail->HardwareID;
1952 while (t && *t && t < (TCHAR *) &detailBuf [sizeof(detailBuf) / sizeof (detailBuf[0])])
1953 {
1954 if (!_tcsicmp(t, DRIVERHWID))
1955 break;
1956
1957 t += _tcslen(t) + 1;
1958 }
1959
1960 if (t && *t && t < (TCHAR *) &detailBuf [sizeof(detailBuf) / sizeof (detailBuf[0])])
1961 {
1962 found = TRUE;
1963 break;
1964 }
1965 }
1966
1967 index ++;
1968 }
1969
1970 if (!found)
1971 SetErrBreak ((tr ("Could not find Host Interface Networking driver! "
1972 "Please reinstall")));
1973
1974 /* set the loopback driver to be the currently selected */
1975 ok = SetupDiSetSelectedDriver (hDeviceInfo, &DeviceInfoData,
1976 &DriverInfoData);
1977 if (!ok)
1978 SetErrBreak (("SetupDiSetSelectedDriver failed (0x%08X)",
1979 GetLastError()));
1980
1981 /* register the phantom device to prepare for install */
1982 ok = SetupDiCallClassInstaller (DIF_REGISTERDEVICE, hDeviceInfo,
1983 &DeviceInfoData);
1984 if (!ok)
1985 SetErrBreak (("SetupDiCallClassInstaller failed (0x%08X)",
1986 GetLastError()));
1987
1988 /* registered, but remove if errors occur in the following code */
1989 registered = TRUE;
1990
1991 /* ask the installer if we can install the device */
1992 ok = SetupDiCallClassInstaller (DIF_ALLOW_INSTALL, hDeviceInfo,
1993 &DeviceInfoData);
1994 if (!ok)
1995 {
1996 if (GetLastError() != ERROR_DI_DO_DEFAULT)
1997 SetErrBreak (("SetupDiCallClassInstaller (DIF_ALLOW_INSTALL) failed (0x%08X)",
1998 GetLastError()));
1999 /* that's fine */
2000 }
2001
2002 /* install the files first */
2003 ok = SetupDiCallClassInstaller (DIF_INSTALLDEVICEFILES, hDeviceInfo,
2004 &DeviceInfoData);
2005 if (!ok)
2006 SetErrBreak (("SetupDiCallClassInstaller (DIF_INSTALLDEVICEFILES) failed (0x%08X)",
2007 GetLastError()));
2008
2009 /* get the device install parameters and disable filecopy */
2010 DeviceInstallParams.cbSize = sizeof(SP_DEVINSTALL_PARAMS);
2011 ok = SetupDiGetDeviceInstallParams (hDeviceInfo, &DeviceInfoData,
2012 &DeviceInstallParams);
2013 if (ok)
2014 {
2015 DeviceInstallParams.Flags |= DI_NOFILECOPY;
2016 ok = SetupDiSetDeviceInstallParams (hDeviceInfo, &DeviceInfoData,
2017 &DeviceInstallParams);
2018 if (!ok)
2019 SetErrBreak (("SetupDiSetDeviceInstallParams failed (0x%08X)",
2020 GetLastError()));
2021 }
2022
2023 /*
2024 * Register any device-specific co-installers for this device,
2025 */
2026
2027 ok = SetupDiCallClassInstaller (DIF_REGISTER_COINSTALLERS,
2028 hDeviceInfo,
2029 &DeviceInfoData);
2030 if (!ok)
2031 SetErrBreak (("SetupDiCallClassInstaller (DIF_REGISTER_COINSTALLERS) failed (0x%08X)",
2032 GetLastError()));
2033
2034 /*
2035 * install any installer-specified interfaces.
2036 * and then do the real install
2037 */
2038 ok = SetupDiCallClassInstaller (DIF_INSTALLINTERFACES,
2039 hDeviceInfo,
2040 &DeviceInfoData);
2041 if (!ok)
2042 SetErrBreak (("SetupDiCallClassInstaller (DIF_INSTALLINTERFACES) failed (0x%08X)",
2043 GetLastError()));
2044
2045 ok = SetupDiCallClassInstaller (DIF_INSTALLDEVICE,
2046 hDeviceInfo,
2047 &DeviceInfoData);
2048 if (!ok)
2049 SetErrBreak (("SetupDiCallClassInstaller (DIF_INSTALLDEVICE) failed (0x%08X)",
2050 GetLastError()));
2051
2052 /* Figure out NetCfgInstanceId */
2053 hkey = SetupDiOpenDevRegKey (hDeviceInfo,
2054 &DeviceInfoData,
2055 DICS_FLAG_GLOBAL,
2056 0,
2057 DIREG_DRV,
2058 KEY_READ);
2059 if (hkey == INVALID_HANDLE_VALUE)
2060 SetErrBreak (("SetupDiOpenDevRegKey failed (0x%08X)",
2061 GetLastError()));
2062
2063 cbSize = sizeof (pCfgGuidString);
2064 DWORD ret;
2065 ret = RegQueryValueEx (hkey, _T ("NetCfgInstanceId"), NULL,
2066 &dwValueType, (LPBYTE) pCfgGuidString, &cbSize);
2067 RegCloseKey (hkey);
2068
2069 ret = RenameConnection (pCfgGuidString, Bstr (aName));
2070 if (FAILED (ret))
2071 SetErrBreak (("Failed to set interface name (ret=0x%08X, "
2072 "pCfgGuidString='%ls', cbSize=%d)",
2073 ret, pCfgGuidString, cbSize));
2074 }
2075 while (0);
2076
2077 /*
2078 * cleanup
2079 */
2080
2081 if (hDeviceInfo != INVALID_HANDLE_VALUE)
2082 {
2083 /* an error has occured, but the device is registered, we must remove it */
2084 if (ret != 0 && registered)
2085 SetupDiCallClassInstaller (DIF_REMOVE, hDeviceInfo, &DeviceInfoData);
2086
2087 found = SetupDiDeleteDeviceInfo (hDeviceInfo, &DeviceInfoData);
2088
2089 /* destroy the driver info list */
2090 if (destroyList)
2091 SetupDiDestroyDriverInfoList (hDeviceInfo, &DeviceInfoData,
2092 SPDIT_CLASSDRIVER);
2093 /* clean up the device info set */
2094 SetupDiDestroyDeviceInfoList (hDeviceInfo);
2095 }
2096
2097 /* return the network connection GUID on success */
2098 if (VBOX_SUCCESS (vrc))
2099 {
2100 /* remove the curly bracket at the end */
2101 pCfgGuidString [_tcslen (pCfgGuidString) - 1] = '\0';
2102 LogFlowFunc (("Network connection GUID string = {%ls}\n", pCfgGuidString + 1));
2103
2104 aGUID = Guid (Utf8Str (pCfgGuidString + 1));
2105 LogFlowFunc (("Network connection GUID = {%Vuuid}\n", aGUID.raw()));
2106 Assert (!aGUID.isEmpty());
2107 }
2108
2109 LogFlowFunc (("vrc=%Vrc\n", vrc));
2110 LogFlowFuncLeave();
2111 return vrc;
2112}
2113
2114/* static */
2115int Host::removeNetworkInterface (SVCHlpClient *aClient,
2116 const Guid &aGUID,
2117 Utf8Str &aErrMsg)
2118{
2119 LogFlowFuncEnter();
2120 LogFlowFunc (("Network connection GUID = {%Vuuid}\n", aGUID.raw()));
2121
2122 AssertReturn (aClient, VERR_INVALID_POINTER);
2123 AssertReturn (!aGUID.isEmpty(), VERR_INVALID_PARAMETER);
2124
2125 int vrc = VINF_SUCCESS;
2126
2127 do
2128 {
2129 TCHAR lszPnPInstanceId [512] = {0};
2130
2131 /* We have to find the device instance ID through a registry search */
2132
2133 HKEY hkeyNetwork = 0;
2134 HKEY hkeyConnection = 0;
2135
2136 do
2137 {
2138 char strRegLocation [256];
2139 sprintf (strRegLocation,
2140 "SYSTEM\\CurrentControlSet\\Control\\Network\\"
2141 "{4D36E972-E325-11CE-BFC1-08002BE10318}\\{%s}",
2142 aGUID.toString().raw());
2143 LONG status;
2144 status = RegOpenKeyExA (HKEY_LOCAL_MACHINE, strRegLocation, 0,
2145 KEY_READ, &hkeyNetwork);
2146 if ((status != ERROR_SUCCESS) || !hkeyNetwork)
2147 SetErrBreak ((
2148 tr ("Host interface network is not found in registry (%s) [1]"),
2149 strRegLocation));
2150
2151 status = RegOpenKeyExA (hkeyNetwork, "Connection", 0,
2152 KEY_READ, &hkeyConnection);
2153 if ((status != ERROR_SUCCESS) || !hkeyConnection)
2154 SetErrBreak ((
2155 tr ("Host interface network is not found in registry (%s) [2]"),
2156 strRegLocation));
2157
2158 DWORD len = sizeof (lszPnPInstanceId);
2159 DWORD dwKeyType;
2160 status = RegQueryValueExW (hkeyConnection, L"PnPInstanceID", NULL,
2161 &dwKeyType, (LPBYTE) lszPnPInstanceId, &len);
2162 if ((status != ERROR_SUCCESS) || (dwKeyType != REG_SZ))
2163 SetErrBreak ((
2164 tr ("Host interface network is not found in registry (%s) [3]"),
2165 strRegLocation));
2166 }
2167 while (0);
2168
2169 if (hkeyConnection)
2170 RegCloseKey (hkeyConnection);
2171 if (hkeyNetwork)
2172 RegCloseKey (hkeyNetwork);
2173
2174 if (VBOX_FAILURE (vrc))
2175 break;
2176
2177 /*
2178 * Now we are going to enumerate all network devices and
2179 * wait until we encounter the right device instance ID
2180 */
2181
2182 HDEVINFO hDeviceInfo = INVALID_HANDLE_VALUE;
2183
2184 do
2185 {
2186 BOOL ok;
2187 DWORD ret = 0;
2188 GUID netGuid;
2189 SP_DEVINFO_DATA DeviceInfoData;
2190 DWORD index = 0;
2191 BOOL found = FALSE;
2192 DWORD size = 0;
2193
2194 /* initialize the structure size */
2195 DeviceInfoData.cbSize = sizeof (SP_DEVINFO_DATA);
2196
2197 /* copy the net class GUID */
2198 memcpy (&netGuid, &GUID_DEVCLASS_NET, sizeof (GUID_DEVCLASS_NET));
2199
2200 /* return a device info set contains all installed devices of the Net class */
2201 hDeviceInfo = SetupDiGetClassDevs (&netGuid, NULL, NULL, DIGCF_PRESENT);
2202
2203 if (hDeviceInfo == INVALID_HANDLE_VALUE)
2204 SetErrBreak (("SetupDiGetClassDevs failed (0x%08X)", GetLastError()));
2205
2206 /* enumerate the driver info list */
2207 while (TRUE)
2208 {
2209 TCHAR *deviceHwid;
2210
2211 ok = SetupDiEnumDeviceInfo (hDeviceInfo, index, &DeviceInfoData);
2212
2213 if (!ok)
2214 {
2215 if (GetLastError() == ERROR_NO_MORE_ITEMS)
2216 break;
2217 else
2218 {
2219 index++;
2220 continue;
2221 }
2222 }
2223
2224 /* try to get the hardware ID registry property */
2225 ok = SetupDiGetDeviceRegistryProperty (hDeviceInfo,
2226 &DeviceInfoData,
2227 SPDRP_HARDWAREID,
2228 NULL,
2229 NULL,
2230 0,
2231 &size);
2232 if (!ok)
2233 {
2234 if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
2235 {
2236 index++;
2237 continue;
2238 }
2239
2240 deviceHwid = (TCHAR *) malloc (size);
2241 ok = SetupDiGetDeviceRegistryProperty (hDeviceInfo,
2242 &DeviceInfoData,
2243 SPDRP_HARDWAREID,
2244 NULL,
2245 (PBYTE)deviceHwid,
2246 size,
2247 NULL);
2248 if (!ok)
2249 {
2250 free (deviceHwid);
2251 deviceHwid = NULL;
2252 index++;
2253 continue;
2254 }
2255 }
2256 else
2257 {
2258 /* something is wrong. This shouldn't have worked with a NULL buffer */
2259 index++;
2260 continue;
2261 }
2262
2263 for (TCHAR *t = deviceHwid;
2264 t && *t && t < &deviceHwid[size / sizeof(TCHAR)];
2265 t += _tcslen (t) + 1)
2266 {
2267 if (!_tcsicmp (DRIVERHWID, t))
2268 {
2269 /* get the device instance ID */
2270 TCHAR devID [MAX_DEVICE_ID_LEN];
2271 if (CM_Get_Device_ID(DeviceInfoData.DevInst,
2272 devID, MAX_DEVICE_ID_LEN, 0) == CR_SUCCESS)
2273 {
2274 /* compare to what we determined before */
2275 if (wcscmp(devID, lszPnPInstanceId) == 0)
2276 {
2277 found = TRUE;
2278 break;
2279 }
2280 }
2281 }
2282 }
2283
2284 if (deviceHwid)
2285 {
2286 free (deviceHwid);
2287 deviceHwid = NULL;
2288 }
2289
2290 if (found)
2291 break;
2292
2293 index++;
2294 }
2295
2296 if (found == FALSE)
2297 SetErrBreak ((tr ("Host Interface Network driver not found (0x%08X)"),
2298 GetLastError()));
2299
2300 ok = SetupDiSetSelectedDevice (hDeviceInfo, &DeviceInfoData);
2301 if (!ok)
2302 SetErrBreak (("SetupDiSetSelectedDevice failed (0x%08X)",
2303 GetLastError()));
2304
2305 ok = SetupDiCallClassInstaller (DIF_REMOVE, hDeviceInfo, &DeviceInfoData);
2306 if (!ok)
2307 SetErrBreak (("SetupDiCallClassInstaller (DIF_REMOVE) failed (0x%08X)",
2308 GetLastError()));
2309 }
2310 while (0);
2311
2312 /* clean up the device info set */
2313 if (hDeviceInfo != INVALID_HANDLE_VALUE)
2314 SetupDiDestroyDeviceInfoList (hDeviceInfo);
2315
2316 if (VBOX_FAILURE (vrc))
2317 break;
2318 }
2319 while (0);
2320
2321 LogFlowFunc (("vrc=%Vrc\n", vrc));
2322 LogFlowFuncLeave();
2323 return vrc;
2324}
2325
2326#undef SetErrBreak
2327
2328/* static */
2329HRESULT Host::networkInterfaceHelperClient (SVCHlpClient *aClient,
2330 Progress *aProgress,
2331 void *aUser, int *aVrc)
2332{
2333 LogFlowFuncEnter();
2334 LogFlowFunc (("aClient={%p}, aProgress={%p}, aUser={%p}\n",
2335 aClient, aProgress, aUser));
2336
2337 AssertReturn ((aClient == NULL && aProgress == NULL && aVrc == NULL) ||
2338 (aClient != NULL && aProgress != NULL && aVrc != NULL),
2339 E_POINTER);
2340 AssertReturn (aUser, E_POINTER);
2341
2342 std::auto_ptr <NetworkInterfaceHelperClientData>
2343 d (static_cast <NetworkInterfaceHelperClientData *> (aUser));
2344
2345 if (aClient == NULL)
2346 {
2347 /* "cleanup only" mode, just return (it will free aUser) */
2348 return S_OK;
2349 }
2350
2351 HRESULT rc = S_OK;
2352 int vrc = VINF_SUCCESS;
2353
2354 switch (d->msgCode)
2355 {
2356 case SVCHlpMsg::CreateHostNetworkInterface:
2357 {
2358 LogFlowFunc (("CreateHostNetworkInterface:\n"));
2359 LogFlowFunc (("Network connection name = '%ls'\n", d->name.raw()));
2360
2361 /* write message and parameters */
2362 vrc = aClient->write (d->msgCode);
2363 if (VBOX_FAILURE (vrc)) break;
2364 vrc = aClient->write (Utf8Str (d->name));
2365 if (VBOX_FAILURE (vrc)) break;
2366
2367 /* wait for a reply */
2368 bool endLoop = false;
2369 while (!endLoop)
2370 {
2371 SVCHlpMsg::Code reply = SVCHlpMsg::Null;
2372
2373 vrc = aClient->read (reply);
2374 if (VBOX_FAILURE (vrc)) break;
2375
2376 switch (reply)
2377 {
2378 case SVCHlpMsg::CreateHostNetworkInterface_OK:
2379 {
2380 /* read the GUID */
2381 Guid guid;
2382 vrc = aClient->read (guid);
2383 if (VBOX_FAILURE (vrc)) break;
2384
2385 LogFlowFunc (("Network connection GUID = {%Vuuid}\n", guid.raw()));
2386
2387 /* initialize the object returned to the caller by
2388 * CreateHostNetworkInterface() */
2389 rc = d->iface->init (d->name, guid);
2390 endLoop = true;
2391 break;
2392 }
2393 case SVCHlpMsg::Error:
2394 {
2395 /* read the error message */
2396 Utf8Str errMsg;
2397 vrc = aClient->read (errMsg);
2398 if (VBOX_FAILURE (vrc)) break;
2399
2400 rc = setError (E_FAIL, errMsg);
2401 endLoop = true;
2402 break;
2403 }
2404 default:
2405 {
2406 endLoop = true;
2407 ComAssertMsgFailedBreak ((
2408 "Invalid message code %d (%08lX)\n",
2409 reply, reply),
2410 rc = E_FAIL);
2411 }
2412 }
2413 }
2414
2415 break;
2416 }
2417 case SVCHlpMsg::RemoveHostNetworkInterface:
2418 {
2419 LogFlowFunc (("RemoveHostNetworkInterface:\n"));
2420 LogFlowFunc (("Network connection GUID = {%Vuuid}\n", d->guid.raw()));
2421
2422 /* write message and parameters */
2423 vrc = aClient->write (d->msgCode);
2424 if (VBOX_FAILURE (vrc)) break;
2425 vrc = aClient->write (d->guid);
2426 if (VBOX_FAILURE (vrc)) break;
2427
2428 /* wait for a reply */
2429 bool endLoop = false;
2430 while (!endLoop)
2431 {
2432 SVCHlpMsg::Code reply = SVCHlpMsg::Null;
2433
2434 vrc = aClient->read (reply);
2435 if (VBOX_FAILURE (vrc)) break;
2436
2437 switch (reply)
2438 {
2439 case SVCHlpMsg::OK:
2440 {
2441 /* no parameters */
2442 rc = S_OK;
2443 endLoop = true;
2444 break;
2445 }
2446 case SVCHlpMsg::Error:
2447 {
2448 /* read the error message */
2449 Utf8Str errMsg;
2450 vrc = aClient->read (errMsg);
2451 if (VBOX_FAILURE (vrc)) break;
2452
2453 rc = setError (E_FAIL, errMsg);
2454 endLoop = true;
2455 break;
2456 }
2457 default:
2458 {
2459 endLoop = true;
2460 ComAssertMsgFailedBreak ((
2461 "Invalid message code %d (%08lX)\n",
2462 reply, reply),
2463 rc = E_FAIL);
2464 }
2465 }
2466 }
2467
2468 break;
2469 }
2470 default:
2471 ComAssertMsgFailedBreak ((
2472 "Invalid message code %d (%08lX)\n",
2473 d->msgCode, d->msgCode),
2474 rc = E_FAIL);
2475 }
2476
2477 if (aVrc)
2478 *aVrc = vrc;
2479
2480 LogFlowFunc (("rc=0x%08X, vrc=%Vrc\n", rc, vrc));
2481 LogFlowFuncLeave();
2482 return rc;
2483}
2484
2485/* static */
2486int Host::networkInterfaceHelperServer (SVCHlpClient *aClient,
2487 SVCHlpMsg::Code aMsgCode)
2488{
2489 LogFlowFuncEnter();
2490 LogFlowFunc (("aClient={%p}, aMsgCode=%d\n", aClient, aMsgCode));
2491
2492 AssertReturn (aClient, VERR_INVALID_POINTER);
2493
2494 int vrc = VINF_SUCCESS;
2495
2496 switch (aMsgCode)
2497 {
2498 case SVCHlpMsg::CreateHostNetworkInterface:
2499 {
2500 LogFlowFunc (("CreateHostNetworkInterface:\n"));
2501
2502 Utf8Str name;
2503 vrc = aClient->read (name);
2504 if (VBOX_FAILURE (vrc)) break;
2505
2506 Guid guid;
2507 Utf8Str errMsg;
2508 vrc = createNetworkInterface (aClient, name, guid, errMsg);
2509
2510 if (VBOX_SUCCESS (vrc))
2511 {
2512 /* write success followed by GUID */
2513 vrc = aClient->write (SVCHlpMsg::CreateHostNetworkInterface_OK);
2514 if (VBOX_FAILURE (vrc)) break;
2515 vrc = aClient->write (guid);
2516 if (VBOX_FAILURE (vrc)) break;
2517 }
2518 else
2519 {
2520 /* write failure followed by error message */
2521 if (errMsg.isEmpty())
2522 errMsg = Utf8StrFmt ("Unspecified error (%Vrc)", vrc);
2523 vrc = aClient->write (SVCHlpMsg::Error);
2524 if (VBOX_FAILURE (vrc)) break;
2525 vrc = aClient->write (errMsg);
2526 if (VBOX_FAILURE (vrc)) break;
2527 }
2528
2529 break;
2530 }
2531 case SVCHlpMsg::RemoveHostNetworkInterface:
2532 {
2533 LogFlowFunc (("RemoveHostNetworkInterface:\n"));
2534
2535 Guid guid;
2536 vrc = aClient->read (guid);
2537 if (VBOX_FAILURE (vrc)) break;
2538
2539 Utf8Str errMsg;
2540 vrc = removeNetworkInterface (aClient, guid, errMsg);
2541
2542 if (VBOX_SUCCESS (vrc))
2543 {
2544 /* write parameter-less success */
2545 vrc = aClient->write (SVCHlpMsg::OK);
2546 if (VBOX_FAILURE (vrc)) break;
2547 }
2548 else
2549 {
2550 /* write failure followed by error message */
2551 if (errMsg.isEmpty())
2552 errMsg = Utf8StrFmt ("Unspecified error (%Vrc)", vrc);
2553 vrc = aClient->write (SVCHlpMsg::Error);
2554 if (VBOX_FAILURE (vrc)) break;
2555 vrc = aClient->write (errMsg);
2556 if (VBOX_FAILURE (vrc)) break;
2557 }
2558
2559 break;
2560 }
2561 default:
2562 AssertMsgFailedBreak ((
2563 "Invalid message code %d (%08lX)\n", aMsgCode, aMsgCode),
2564 VERR_GENERAL_FAILURE);
2565 }
2566
2567 LogFlowFunc (("vrc=%Vrc\n", vrc));
2568 LogFlowFuncLeave();
2569 return vrc;
2570}
2571
2572#endif /* __WIN__ */
2573
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