VirtualBox

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

Last change on this file since 2042 was 1906, checked in by vboxsync, 18 years ago

The IHost part of #1912.

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