VirtualBox

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

Last change on this file since 6290 was 6076, checked in by vboxsync, 17 years ago

Merged dmik/s2 branch (r25959:26751) to the trunk.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 108.9 KB
Line 
1/** @file
2 * VirtualBox COM class implementation
3 */
4
5/*
6 * Copyright (C) 2006-2007 innotek 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 (GPL) as published by the Free Software
12 * Foundation, in version 2 as it comes in the "COPYING" file of the
13 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
14 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
15 */
16
17#ifdef RT_OS_LINUX
18#include <sys/types.h>
19#include <sys/stat.h>
20#include <unistd.h>
21#include <sys/ioctl.h>
22#include <fcntl.h>
23#include <mntent.h>
24/* bird: This is a hack to work around conflicts between these linux kernel headers
25 * and the GLIBC tcpip headers. They have different declarations of the 4
26 * standard byte order functions. */
27#define _LINUX_BYTEORDER_GENERIC_H
28#include <linux/cdrom.h>
29#ifdef VBOX_USE_LIBHAL
30// # include <libhal.h>
31// /* These are defined by libhal.h and by VBox header files. */
32// # undef TRUE
33// # undef FALSE
34#include "vbox-libhal.h"
35#endif
36#include <errno.h>
37#endif /* RT_OS_LINUX */
38
39#ifdef RT_OS_SOLARIS
40# include <fcntl.h>
41# include <unistd.h>
42# include <stropts.h>
43# include <errno.h>
44# include <limits.h>
45# include <stdio.h>
46# include <sys/types.h>
47# include <sys/stat.h>
48# include <sys/cdio.h>
49# include <sys/dkio.h>
50# include <sys/mnttab.h>
51# include <sys/mntent.h>
52# ifdef VBOX_USE_LIBHAL
53# include "vbox-libhal.h"
54extern "C" char *getfullrawname(char *);
55# endif
56#endif /* RT_OS_SOLARIS */
57
58#ifdef RT_OS_WINDOWS
59#define _WIN32_DCOM
60#include <windows.h>
61#include <shellapi.h>
62#define INITGUID
63#include <guiddef.h>
64#include <devguid.h>
65#include <objbase.h>
66#include <setupapi.h>
67#include <shlobj.h>
68#include <cfgmgr32.h>
69#endif /* RT_OS_WINDOWS */
70
71
72#include "HostImpl.h"
73#include "HostDVDDriveImpl.h"
74#include "HostFloppyDriveImpl.h"
75#include "HostUSBDeviceImpl.h"
76#include "USBControllerImpl.h"
77#include "USBDeviceFilterImpl.h"
78#include "USBProxyService.h"
79#include "VirtualBoxImpl.h"
80#include "MachineImpl.h"
81#include "Logging.h"
82
83#ifdef RT_OS_DARWIN
84#include "darwin/iokit.h"
85#endif
86
87#ifdef RT_OS_WINDOWS
88#include "HostNetworkInterfaceImpl.h"
89#endif
90
91#include <VBox/usb.h>
92#include <VBox/err.h>
93#include <iprt/string.h>
94#include <iprt/system.h>
95#include <iprt/time.h>
96#include <iprt/param.h>
97#include <iprt/env.h>
98#ifdef RT_OS_SOLARIS
99# include <iprt/path.h>
100#endif
101
102#include <stdio.h>
103
104#include <algorithm>
105
106// constructor / destructor
107/////////////////////////////////////////////////////////////////////////////
108
109HRESULT Host::FinalConstruct()
110{
111 return S_OK;
112}
113
114void Host::FinalRelease()
115{
116 if (isReady())
117 uninit();
118}
119
120// public initializer/uninitializer for internal purposes only
121/////////////////////////////////////////////////////////////////////////////
122
123/**
124 * Initializes the host object.
125 *
126 * @returns COM result indicator
127 * @param parent handle of our parent object
128 */
129HRESULT Host::init (VirtualBox *parent)
130{
131 LogFlowThisFunc (("isReady=%d\n", isReady()));
132
133 ComAssertRet (parent, E_INVALIDARG);
134
135 AutoLock lock(this);
136 ComAssertRet (!isReady(), E_UNEXPECTED);
137
138 mParent = parent;
139
140#if defined (RT_OS_DARWIN) && defined (VBOX_WITH_USB)
141 mUSBProxyService = new USBProxyServiceDarwin (this);
142#elif defined (RT_OS_LINUX) && defined (VBOX_WITH_USB)
143 mUSBProxyService = new USBProxyServiceLinux (this);
144#elif defined (RT_OS_OS2) && defined (VBOX_WITH_USB)
145 mUSBProxyService = new USBProxyServiceOs2 (this);
146#elif defined (RT_OS_WINDOWS) && defined (VBOX_WITH_USB)
147 mUSBProxyService = new USBProxyServiceWin32 (this);
148#else
149 mUSBProxyService = new USBProxyService (this);
150#endif
151 /** @todo handle !mUSBProxySerivce->isActive() and mUSBProxyService->getLastError()
152 * and somehow report or whatever that the proxy failed to startup.
153 * Also, there might be init order issues... */
154
155 setReady(true);
156 return S_OK;
157}
158
159/**
160 * Uninitializes the host object and sets the ready flag to FALSE.
161 * Called either from FinalRelease() or by the parent when it gets destroyed.
162 */
163void Host::uninit()
164{
165 LogFlowThisFunc (("isReady=%d\n", isReady()));
166
167 AssertReturn (isReady(), (void) 0);
168
169 /* wait for USB proxy service to terminate before we uninit all USB
170 * devices */
171 LogFlowThisFunc (("Stopping USB proxy service...\n"));
172 delete mUSBProxyService;
173 LogFlowThisFunc (("Done stopping USB proxy service.\n"));
174 mUSBProxyService = NULL;
175
176 /* uninit all USB device filters still referenced by clients */
177 uninitDependentChildren();
178
179 mUSBDeviceFilters.clear();
180 mUSBDevices.clear();
181
182 setReady (FALSE);
183}
184
185// IHost properties
186/////////////////////////////////////////////////////////////////////////////
187
188/**
189 * Returns a list of host DVD drives.
190 *
191 * @returns COM status code
192 * @param drives address of result pointer
193 */
194STDMETHODIMP Host::COMGETTER(DVDDrives) (IHostDVDDriveCollection **drives)
195{
196 if (!drives)
197 return E_POINTER;
198 AutoLock lock(this);
199 CHECK_READY();
200 std::list <ComObjPtr <HostDVDDrive> > list;
201
202#if defined(RT_OS_WINDOWS)
203 int sz = GetLogicalDriveStrings(0, NULL);
204 TCHAR *hostDrives = new TCHAR[sz+1];
205 GetLogicalDriveStrings(sz, hostDrives);
206 wchar_t driveName[3] = { '?', ':', '\0' };
207 TCHAR *p = hostDrives;
208 do
209 {
210 if (GetDriveType(p) == DRIVE_CDROM)
211 {
212 driveName[0] = *p;
213 ComObjPtr <HostDVDDrive> hostDVDDriveObj;
214 hostDVDDriveObj.createObject();
215 hostDVDDriveObj->init (Bstr (driveName));
216 list.push_back (hostDVDDriveObj);
217 }
218 p += _tcslen(p) + 1;
219 }
220 while (*p);
221 delete[] hostDrives;
222
223#elif defined(RT_OS_SOLARIS)
224# ifdef VBOX_USE_LIBHAL
225 if (!getDVDInfoFromHal(list))
226# endif
227 // Not all Solaris versions ship with libhal.
228 // So use a fallback approach similar to Linux.
229 {
230 if (RTEnvGet("VBOX_CDROM"))
231 {
232 char *cdromEnv = strdup(RTEnvGet("VBOX_CDROM"));
233 char *cdromDrive;
234 cdromDrive = strtok(cdromEnv, ":"); /** @todo use strtok_r. */
235 while (cdromDrive)
236 {
237 if (validateDevice(cdromDrive, true))
238 {
239 ComObjPtr <HostDVDDrive> hostDVDDriveObj;
240 hostDVDDriveObj.createObject();
241 hostDVDDriveObj->init (Bstr (cdromDrive));
242 list.push_back (hostDVDDriveObj);
243 }
244 cdromDrive = strtok(NULL, ":");
245 }
246 free(cdromEnv);
247 }
248 else
249 {
250 // this might work on Solaris version older than Nevada.
251 if (validateDevice("/cdrom/cdrom0", true))
252 {
253 ComObjPtr <HostDVDDrive> hostDVDDriveObj;
254 hostDVDDriveObj.createObject();
255 hostDVDDriveObj->init (Bstr ("cdrom/cdrom0"));
256 list.push_back (hostDVDDriveObj);
257 }
258
259 // check the mounted drives
260 parseMountTable(MNTTAB, list);
261 }
262 }
263
264#elif defined(RT_OS_LINUX)
265#ifdef VBOX_USE_LIBHAL
266 if (!getDVDInfoFromHal(list)) /* Playing with #defines in this way is nasty, I know. */
267#endif /* USE_LIBHAL defined */
268 // On Linux without hal, the situation is much more complex. We will take a
269 // heuristical approach and also allow the user to specify a list of host
270 // CDROMs using an environment variable.
271 // The general strategy is to try some known device names and see of they
272 // exist. At last, we'll enumerate the /etc/fstab file (luckily there's an
273 // API to parse it) for CDROM devices. Ok, let's start!
274
275 {
276 if (RTEnvGet("VBOX_CDROM"))
277 {
278 char *cdromEnv = strdupa(RTEnvGet("VBOX_CDROM"));
279 char *cdromDrive;
280 cdromDrive = strtok(cdromEnv, ":"); /** @todo use strtok_r */
281 while (cdromDrive)
282 {
283 if (validateDevice(cdromDrive, true))
284 {
285 ComObjPtr <HostDVDDrive> hostDVDDriveObj;
286 hostDVDDriveObj.createObject();
287 hostDVDDriveObj->init (Bstr (cdromDrive));
288 list.push_back (hostDVDDriveObj);
289 }
290 cdromDrive = strtok(NULL, ":");
291 }
292 }
293 else
294 {
295 // this is a good guess usually
296 if (validateDevice("/dev/cdrom", true))
297 {
298 ComObjPtr <HostDVDDrive> hostDVDDriveObj;
299 hostDVDDriveObj.createObject();
300 hostDVDDriveObj->init (Bstr ("/dev/cdrom"));
301 list.push_back (hostDVDDriveObj);
302 }
303
304 // check the mounted drives
305 parseMountTable((char*)"/etc/mtab", list);
306
307 // check the drives that can be mounted
308 parseMountTable((char*)"/etc/fstab", list);
309 }
310 }
311#elif defined(RT_OS_DARWIN)
312 PDARWINDVD cur = DarwinGetDVDDrives();
313 while (cur)
314 {
315 ComObjPtr<HostDVDDrive> hostDVDDriveObj;
316 hostDVDDriveObj.createObject();
317 hostDVDDriveObj->init(Bstr(cur->szName));
318 list.push_back(hostDVDDriveObj);
319
320 /* next */
321 void *freeMe = cur;
322 cur = cur->pNext;
323 RTMemFree(freeMe);
324 }
325
326#else
327 /* PORTME */
328#endif
329
330 ComObjPtr<HostDVDDriveCollection> collection;
331 collection.createObject();
332 collection->init (list);
333 collection.queryInterfaceTo(drives);
334 return S_OK;
335}
336
337/**
338 * Returns a list of host floppy drives.
339 *
340 * @returns COM status code
341 * @param drives address of result pointer
342 */
343STDMETHODIMP Host::COMGETTER(FloppyDrives) (IHostFloppyDriveCollection **drives)
344{
345 if (!drives)
346 return E_POINTER;
347 AutoLock lock(this);
348 CHECK_READY();
349
350 std::list <ComObjPtr <HostFloppyDrive> > list;
351
352#ifdef RT_OS_WINDOWS
353 int sz = GetLogicalDriveStrings(0, NULL);
354 TCHAR *hostDrives = new TCHAR[sz+1];
355 GetLogicalDriveStrings(sz, hostDrives);
356 wchar_t driveName[3] = { '?', ':', '\0' };
357 TCHAR *p = hostDrives;
358 do
359 {
360 if (GetDriveType(p) == DRIVE_REMOVABLE)
361 {
362 driveName[0] = *p;
363 ComObjPtr <HostFloppyDrive> hostFloppyDriveObj;
364 hostFloppyDriveObj.createObject();
365 hostFloppyDriveObj->init (Bstr (driveName));
366 list.push_back (hostFloppyDriveObj);
367 }
368 p += _tcslen(p) + 1;
369 }
370 while (*p);
371 delete[] hostDrives;
372#elif defined(RT_OS_LINUX)
373#ifdef VBOX_USE_LIBHAL
374 if (!getFloppyInfoFromHal(list)) /* Playing with #defines in this way is nasty, I know. */
375#endif /* USE_LIBHAL defined */
376 // As with the CDROMs, on Linux we have to take a multi-level approach
377 // involving parsing the mount tables. As this is not bulletproof, we'll
378 // give the user the chance to override the detection by an environment
379 // variable and skip the detection.
380
381 {
382 if (RTEnvGet("VBOX_FLOPPY"))
383 {
384 char *floppyEnv = strdupa(RTEnvGet("VBOX_FLOPPY"));
385 char *floppyDrive;
386 floppyDrive = strtok(floppyEnv, ":");
387 while (floppyDrive)
388 {
389 // check if this is an acceptable device
390 if (validateDevice(floppyDrive, false))
391 {
392 ComObjPtr <HostFloppyDrive> hostFloppyDriveObj;
393 hostFloppyDriveObj.createObject();
394 hostFloppyDriveObj->init (Bstr (floppyDrive));
395 list.push_back (hostFloppyDriveObj);
396 }
397 floppyDrive = strtok(NULL, ":");
398 }
399 }
400 else
401 {
402 // we assume that a floppy is always /dev/fd[x] with x from 0 to 7
403 char devName[10];
404 for (int i = 0; i <= 7; i++)
405 {
406 sprintf(devName, "/dev/fd%d", i);
407 if (validateDevice(devName, false))
408 {
409 ComObjPtr <HostFloppyDrive> hostFloppyDriveObj;
410 hostFloppyDriveObj.createObject();
411 hostFloppyDriveObj->init (Bstr (devName));
412 list.push_back (hostFloppyDriveObj);
413 }
414 }
415 }
416 }
417#else
418 /* PORTME */
419#endif
420
421 ComObjPtr<HostFloppyDriveCollection> collection;
422 collection.createObject();
423 collection->init (list);
424 collection.queryInterfaceTo(drives);
425 return S_OK;
426}
427
428#ifdef RT_OS_WINDOWS
429
430static bool IsTAPDevice(const char *guid)
431{
432 HKEY hNetcard;
433 LONG status;
434 DWORD len;
435 int i = 0;
436 bool ret = false;
437
438 status = RegOpenKeyExA(HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E972-E325-11CE-BFC1-08002BE10318}", 0, KEY_READ, &hNetcard);
439 if (status != ERROR_SUCCESS)
440 return false;
441
442 while(true)
443 {
444 char szEnumName[256];
445 char szNetCfgInstanceId[256];
446 DWORD dwKeyType;
447 HKEY hNetCardGUID;
448
449 len = sizeof(szEnumName);
450 status = RegEnumKeyExA(hNetcard, i, szEnumName, &len, NULL, NULL, NULL, NULL);
451 if (status != ERROR_SUCCESS)
452 break;
453
454 status = RegOpenKeyExA(hNetcard, szEnumName, 0, KEY_READ, &hNetCardGUID);
455 if (status == ERROR_SUCCESS)
456 {
457 len = sizeof (szNetCfgInstanceId);
458 status = RegQueryValueExA(hNetCardGUID, "NetCfgInstanceId", NULL, &dwKeyType, (LPBYTE)szNetCfgInstanceId, &len);
459 if (status == ERROR_SUCCESS && dwKeyType == REG_SZ)
460 {
461 char szNetProductName[256];
462 char szNetProviderName[256];
463
464 szNetProductName[0] = 0;
465 len = sizeof(szNetProductName);
466 status = RegQueryValueExA(hNetCardGUID, "ProductName", NULL, &dwKeyType, (LPBYTE)szNetProductName, &len);
467
468 szNetProviderName[0] = 0;
469 len = sizeof(szNetProviderName);
470 status = RegQueryValueExA(hNetCardGUID, "ProviderName", NULL, &dwKeyType, (LPBYTE)szNetProviderName, &len);
471
472 if ( !strcmp(szNetCfgInstanceId, guid)
473 && !strcmp(szNetProductName, "VirtualBox TAP Adapter")
474 && !strcmp(szNetProviderName, "innotek GmbH"))
475 {
476 ret = true;
477 RegCloseKey(hNetCardGUID);
478 break;
479 }
480 }
481 RegCloseKey(hNetCardGUID);
482 }
483 ++i;
484 }
485
486 RegCloseKey (hNetcard);
487 return ret;
488}
489
490/**
491 * Returns a list of host network interfaces.
492 *
493 * @returns COM status code
494 * @param drives address of result pointer
495 */
496STDMETHODIMP Host::COMGETTER(NetworkInterfaces) (IHostNetworkInterfaceCollection **networkInterfaces)
497{
498 if (!networkInterfaces)
499 return E_POINTER;
500 AutoLock lock(this);
501 CHECK_READY();
502
503 std::list <ComObjPtr <HostNetworkInterface> > list;
504
505 static const char *NetworkKey = "SYSTEM\\CurrentControlSet\\Control\\Network\\"
506 "{4D36E972-E325-11CE-BFC1-08002BE10318}";
507 HKEY hCtrlNet;
508 LONG status;
509 DWORD len;
510 status = RegOpenKeyExA (HKEY_LOCAL_MACHINE, NetworkKey, 0, KEY_READ, &hCtrlNet);
511 if (status != ERROR_SUCCESS)
512 return setError (E_FAIL, tr("Could not open registry key \"%s\""), NetworkKey);
513
514 for (int i = 0;; ++ i)
515 {
516 char szNetworkGUID [256];
517 HKEY hConnection;
518 char szNetworkConnection [256];
519
520 len = sizeof (szNetworkGUID);
521 status = RegEnumKeyExA (hCtrlNet, i, szNetworkGUID, &len, NULL, NULL, NULL, NULL);
522 if (status != ERROR_SUCCESS)
523 break;
524
525 if (!IsTAPDevice(szNetworkGUID))
526 continue;
527
528 RTStrPrintf (szNetworkConnection, sizeof (szNetworkConnection),
529 "%s\\Connection", szNetworkGUID);
530 status = RegOpenKeyExA (hCtrlNet, szNetworkConnection, 0, KEY_READ, &hConnection);
531 if (status == ERROR_SUCCESS)
532 {
533 DWORD dwKeyType;
534 status = RegQueryValueExW (hConnection, TEXT("Name"), NULL,
535 &dwKeyType, NULL, &len);
536 if (status == ERROR_SUCCESS && dwKeyType == REG_SZ)
537 {
538 size_t uniLen = (len + sizeof (OLECHAR) - 1) / sizeof (OLECHAR);
539 Bstr name (uniLen + 1 /* extra zero */);
540 status = RegQueryValueExW (hConnection, TEXT("Name"), NULL,
541 &dwKeyType, (LPBYTE) name.mutableRaw(), &len);
542 if (status == ERROR_SUCCESS)
543 {
544RTLogPrintf("Connection name %ls\n", name.mutableRaw());
545 /* put a trailing zero, just in case (see MSDN) */
546 name.mutableRaw() [uniLen] = 0;
547 /* create a new object and add it to the list */
548 ComObjPtr <HostNetworkInterface> iface;
549 iface.createObject();
550 /* remove the curly bracket at the end */
551 szNetworkGUID [strlen(szNetworkGUID) - 1] = '\0';
552 if (SUCCEEDED (iface->init (name, Guid (szNetworkGUID + 1))))
553 list.push_back (iface);
554 }
555 }
556 RegCloseKey (hConnection);
557 }
558 }
559 RegCloseKey (hCtrlNet);
560
561 ComObjPtr <HostNetworkInterfaceCollection> collection;
562 collection.createObject();
563 collection->init (list);
564 collection.queryInterfaceTo (networkInterfaces);
565 return S_OK;
566}
567#endif /* RT_OS_WINDOWS */
568
569STDMETHODIMP Host::COMGETTER(USBDevices)(IHostUSBDeviceCollection **aUSBDevices)
570{
571#ifdef VBOX_WITH_USB
572 if (!aUSBDevices)
573 return E_POINTER;
574
575 AutoLock alock (this);
576 CHECK_READY();
577
578 HRESULT rc = checkUSBProxyService();
579 CheckComRCReturnRC (rc);
580
581 ComObjPtr <HostUSBDeviceCollection> collection;
582 collection.createObject();
583 collection->init (mUSBDevices);
584 collection.queryInterfaceTo (aUSBDevices);
585 return S_OK;
586#else
587 /* Note: The GUI depends on this method returning E_NOTIMPL with no
588 * extended error info to indicate that USB is simply not available
589 * (w/o treting it as a failure), for example, as in OSE */
590 return E_NOTIMPL;
591#endif
592}
593
594STDMETHODIMP Host::COMGETTER(USBDeviceFilters) (IHostUSBDeviceFilterCollection ** aUSBDeviceFilters)
595{
596#ifdef VBOX_WITH_USB
597 if (!aUSBDeviceFilters)
598 return E_POINTER;
599
600 AutoLock alock (this);
601 CHECK_READY();
602
603 HRESULT rc = checkUSBProxyService();
604 CheckComRCReturnRC (rc);
605
606 ComObjPtr <HostUSBDeviceFilterCollection> collection;
607 collection.createObject();
608 collection->init (mUSBDeviceFilters);
609 collection.queryInterfaceTo (aUSBDeviceFilters);
610 return S_OK;
611#else
612 /* Note: The GUI depends on this method returning E_NOTIMPL with no
613 * extended error info to indicate that USB is simply not available
614 * (w/o treting it as a failure), for example, as in OSE */
615 return E_NOTIMPL;
616#endif
617}
618
619/**
620 * Returns the number of installed logical processors
621 *
622 * @returns COM status code
623 * @param count address of result variable
624 */
625STDMETHODIMP Host::COMGETTER(ProcessorCount)(ULONG *count)
626{
627 if (!count)
628 return E_POINTER;
629 AutoLock lock(this);
630 CHECK_READY();
631 *count = RTSystemProcessorGetCount();
632 return S_OK;
633}
634
635/**
636 * Returns the (approximate) speed of the host CPU in MHz
637 *
638 * @returns COM status code
639 * @param speed address of result variable
640 */
641STDMETHODIMP Host::COMGETTER(ProcessorSpeed)(ULONG *speed)
642{
643 if (!speed)
644 return E_POINTER;
645 AutoLock lock(this);
646 CHECK_READY();
647 /** @todo Add a runtime function for this which uses GIP. */
648 return S_OK;
649}
650/**
651 * Returns a description string for the host CPU
652 *
653 * @returns COM status code
654 * @param description address of result variable
655 */
656STDMETHODIMP Host::COMGETTER(ProcessorDescription)(BSTR *description)
657{
658 if (!description)
659 return E_POINTER;
660 AutoLock lock(this);
661 CHECK_READY();
662 /** @todo */
663 return S_OK;
664}
665
666
667/**
668 * Returns the amount of installed system memory in megabytes
669 *
670 * @returns COM status code
671 * @param size address of result variable
672 */
673STDMETHODIMP Host::COMGETTER(MemorySize)(ULONG *size)
674{
675 if (!size)
676 return E_POINTER;
677 AutoLock lock(this);
678 CHECK_READY();
679 /** @todo */
680 return S_OK;
681}
682
683/**
684 * Returns the current system memory free space in megabytes
685 *
686 * @returns COM status code
687 * @param available address of result variable
688 */
689STDMETHODIMP Host::COMGETTER(MemoryAvailable)(ULONG *available)
690{
691 if (!available)
692 return E_POINTER;
693 AutoLock lock(this);
694 CHECK_READY();
695 /** @todo */
696 return S_OK;
697}
698
699/**
700 * Returns the name string of the host operating system
701 *
702 * @returns COM status code
703 * @param os address of result variable
704 */
705STDMETHODIMP Host::COMGETTER(OperatingSystem)(BSTR *os)
706{
707 if (!os)
708 return E_POINTER;
709 AutoLock lock(this);
710 CHECK_READY();
711 /** @todo */
712 return S_OK;
713}
714
715/**
716 * Returns the version string of the host operating system
717 *
718 * @returns COM status code
719 * @param os address of result variable
720 */
721STDMETHODIMP Host::COMGETTER(OSVersion)(BSTR *version)
722{
723 if (!version)
724 return E_POINTER;
725 AutoLock lock(this);
726 CHECK_READY();
727 /** @todo */
728 return S_OK;
729}
730
731/**
732 * Returns the current host time in milliseconds since 1970-01-01 UTC.
733 *
734 * @returns COM status code
735 * @param time address of result variable
736 */
737STDMETHODIMP Host::COMGETTER(UTCTime)(LONG64 *aUTCTime)
738{
739 if (!aUTCTime)
740 return E_POINTER;
741 AutoLock lock(this);
742 CHECK_READY();
743 RTTIMESPEC now;
744 *aUTCTime = RTTimeSpecGetMilli(RTTimeNow(&now));
745 return S_OK;
746}
747
748// IHost methods
749////////////////////////////////////////////////////////////////////////////////
750
751#ifdef RT_OS_WINDOWS
752
753/**
754 * Returns TRUE if the Windows version is 6.0 or greater (i.e. it's Vista and
755 * later OSes) and it has the UAC (User Account Control) feature enabled.
756 */
757static BOOL IsUACEnabled()
758{
759 LONG rc = 0;
760
761 OSVERSIONINFOEX info;
762 ZeroMemory (&info, sizeof (OSVERSIONINFOEX));
763 info.dwOSVersionInfoSize = sizeof (OSVERSIONINFOEX);
764 rc = GetVersionEx ((OSVERSIONINFO *) &info);
765 AssertReturn (rc != 0, FALSE);
766
767 LogFlowFunc (("dwMajorVersion=%d, dwMinorVersion=%d\n",
768 info.dwMajorVersion, info.dwMinorVersion));
769
770 /* we are interested only in Vista (and newer versions...). In all
771 * earlier versions UAC is not present. */
772 if (info.dwMajorVersion < 6)
773 return FALSE;
774
775 /* the default EnableLUA value is 1 (Enabled) */
776 DWORD dwEnableLUA = 1;
777
778 HKEY hKey;
779 rc = RegOpenKeyExA (HKEY_LOCAL_MACHINE,
780 "Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System",
781 0, KEY_QUERY_VALUE, &hKey);
782
783 Assert (rc == ERROR_SUCCESS || rc == ERROR_PATH_NOT_FOUND);
784 if (rc == ERROR_SUCCESS)
785 {
786
787 DWORD cbEnableLUA = sizeof (dwEnableLUA);
788 rc = RegQueryValueExA (hKey, "EnableLUA", NULL, NULL,
789 (LPBYTE) &dwEnableLUA, &cbEnableLUA);
790
791 RegCloseKey (hKey);
792
793 Assert (rc == ERROR_SUCCESS || rc == ERROR_FILE_NOT_FOUND);
794 }
795
796 LogFlowFunc (("rc=%d, dwEnableLUA=%d\n", rc, dwEnableLUA));
797
798 return dwEnableLUA == 1;
799}
800
801struct NetworkInterfaceHelperClientData
802{
803 SVCHlpMsg::Code msgCode;
804 /* for SVCHlpMsg::CreateHostNetworkInterface */
805 Bstr name;
806 ComObjPtr <HostNetworkInterface> iface;
807 /* for SVCHlpMsg::RemoveHostNetworkInterface */
808 Guid guid;
809};
810
811STDMETHODIMP
812Host::CreateHostNetworkInterface (INPTR BSTR aName,
813 IHostNetworkInterface **aHostNetworkInterface,
814 IProgress **aProgress)
815{
816 if (!aName)
817 return E_INVALIDARG;
818 if (!aHostNetworkInterface)
819 return E_POINTER;
820 if (!aProgress)
821 return E_POINTER;
822
823 AutoLock lock (this);
824 CHECK_READY();
825
826 HRESULT rc = S_OK;
827
828 /* first check whether an interface with the given name already exists */
829 {
830 ComPtr <IHostNetworkInterfaceCollection> coll;
831 rc = COMGETTER(NetworkInterfaces) (coll.asOutParam());
832 CheckComRCReturnRC (rc);
833 ComPtr <IHostNetworkInterface> iface;
834 if (SUCCEEDED (coll->FindByName (aName, iface.asOutParam())))
835 return setError (E_FAIL,
836 tr ("Host network interface '%ls' already exists"), aName);
837 }
838
839 /* create a progress object */
840 ComObjPtr <Progress> progress;
841 progress.createObject();
842 rc = progress->init (mParent, (IHost *) this,
843 Bstr (tr ("Creating host network interface")),
844 FALSE /* aCancelable */);
845 CheckComRCReturnRC (rc);
846 progress.queryInterfaceTo (aProgress);
847
848 /* create a new uninitialized host interface object */
849 ComObjPtr <HostNetworkInterface> iface;
850 iface.createObject();
851 iface.queryInterfaceTo (aHostNetworkInterface);
852
853 /* create the networkInterfaceHelperClient() argument */
854 std::auto_ptr <NetworkInterfaceHelperClientData>
855 d (new NetworkInterfaceHelperClientData());
856 AssertReturn (d.get(), E_OUTOFMEMORY);
857
858 d->msgCode = SVCHlpMsg::CreateHostNetworkInterface;
859 d->name = aName;
860 d->iface = iface;
861
862 rc = mParent->startSVCHelperClient (
863 IsUACEnabled() == TRUE /* aPrivileged */,
864 networkInterfaceHelperClient,
865 static_cast <void *> (d.get()),
866 progress);
867
868 if (SUCCEEDED (rc))
869 {
870 /* d is now owned by networkInterfaceHelperClient(), so release it */
871 d.release();
872 }
873
874 return rc;
875}
876
877STDMETHODIMP
878Host::RemoveHostNetworkInterface (INPTR GUIDPARAM aId,
879 IHostNetworkInterface **aHostNetworkInterface,
880 IProgress **aProgress)
881{
882 if (!aHostNetworkInterface)
883 return E_POINTER;
884 if (!aProgress)
885 return E_POINTER;
886
887 AutoLock lock (this);
888 CHECK_READY();
889
890 HRESULT rc = S_OK;
891
892 /* first check whether an interface with the given name already exists */
893 {
894 ComPtr <IHostNetworkInterfaceCollection> coll;
895 rc = COMGETTER(NetworkInterfaces) (coll.asOutParam());
896 CheckComRCReturnRC (rc);
897 ComPtr <IHostNetworkInterface> iface;
898 if (FAILED (coll->FindById (aId, iface.asOutParam())))
899 return setError (E_FAIL,
900 tr ("Host network interface with UUID {%Vuuid} does not exist"),
901 Guid (aId).raw());
902
903 /* return the object to be removed to the caller */
904 iface.queryInterfaceTo (aHostNetworkInterface);
905 }
906
907 /* create a progress object */
908 ComObjPtr <Progress> progress;
909 progress.createObject();
910 rc = progress->init (mParent, (IHost *) this,
911 Bstr (tr ("Removing host network interface")),
912 FALSE /* aCancelable */);
913 CheckComRCReturnRC (rc);
914 progress.queryInterfaceTo (aProgress);
915
916 /* create the networkInterfaceHelperClient() argument */
917 std::auto_ptr <NetworkInterfaceHelperClientData>
918 d (new NetworkInterfaceHelperClientData());
919 AssertReturn (d.get(), E_OUTOFMEMORY);
920
921 d->msgCode = SVCHlpMsg::RemoveHostNetworkInterface;
922 d->guid = aId;
923
924 rc = mParent->startSVCHelperClient (
925 IsUACEnabled() == TRUE /* aPrivileged */,
926 networkInterfaceHelperClient,
927 static_cast <void *> (d.get()),
928 progress);
929
930 if (SUCCEEDED (rc))
931 {
932 /* d is now owned by networkInterfaceHelperClient(), so release it */
933 d.release();
934 }
935
936 return rc;
937}
938
939#endif /* RT_OS_WINDOWS */
940
941STDMETHODIMP Host::CreateUSBDeviceFilter (INPTR BSTR aName, IHostUSBDeviceFilter **aFilter)
942{
943#ifdef VBOX_WITH_USB
944 if (!aFilter)
945 return E_POINTER;
946
947 if (!aName || *aName == 0)
948 return E_INVALIDARG;
949
950 AutoLock lock (this);
951 CHECK_READY();
952
953 HRESULT rc = checkUSBProxyService();
954 CheckComRCReturnRC (rc);
955
956 ComObjPtr <HostUSBDeviceFilter> filter;
957 filter.createObject();
958 rc = filter->init (this, aName);
959 ComAssertComRCRet (rc, rc);
960 rc = filter.queryInterfaceTo (aFilter);
961 AssertComRCReturn (rc, rc);
962 return S_OK;
963#else
964 /* Note: The GUI depends on this method returning E_NOTIMPL with no
965 * extended error info to indicate that USB is simply not available
966 * (w/o treting it as a failure), for example, as in OSE */
967 return E_NOTIMPL;
968#endif
969}
970
971STDMETHODIMP Host::InsertUSBDeviceFilter (ULONG aPosition, IHostUSBDeviceFilter *aFilter)
972{
973#ifdef VBOX_WITH_USB
974 if (!aFilter)
975 return E_INVALIDARG;
976
977 AutoLock alock (this);
978 CHECK_READY();
979
980 HRESULT rc = checkUSBProxyService();
981 CheckComRCReturnRC (rc);
982
983 ComObjPtr <HostUSBDeviceFilter> filter = getDependentChild (aFilter);
984 if (!filter)
985 return setError (E_INVALIDARG,
986 tr ("The given USB device filter is not created within "
987 "this VirtualBox instance"));
988
989 if (filter->mInList)
990 return setError (E_INVALIDARG,
991 tr ("The given USB device filter is already in the list"));
992
993 /* iterate to the position... */
994 USBDeviceFilterList::iterator it = mUSBDeviceFilters.begin();
995 std::advance (it, aPosition);
996 /* ...and insert */
997 mUSBDeviceFilters.insert (it, filter);
998 filter->mInList = true;
999
1000 /* notify the proxy (only when the filter is active) */
1001 if (filter->data().mActive)
1002 {
1003 ComAssertRet (filter->id() == NULL, E_FAIL);
1004#ifndef VBOX_WITH_USBFILTER
1005 filter->id() =
1006 mUSBProxyService->insertFilter (ComPtr <IUSBDeviceFilter> (aFilter));
1007#else
1008 filter->id() = mUSBProxyService->insertFilter (&filter->data().mUSBFilter);
1009#endif
1010 }
1011
1012 /* save the global settings */
1013 alock.unlock();
1014 return mParent->saveSettings();
1015#else
1016 /* Note: The GUI depends on this method returning E_NOTIMPL with no
1017 * extended error info to indicate that USB is simply not available
1018 * (w/o treting it as a failure), for example, as in OSE */
1019 return E_NOTIMPL;
1020#endif
1021}
1022
1023STDMETHODIMP Host::RemoveUSBDeviceFilter (ULONG aPosition, IHostUSBDeviceFilter **aFilter)
1024{
1025#ifdef VBOX_WITH_USB
1026 if (!aFilter)
1027 return E_POINTER;
1028
1029 AutoLock alock (this);
1030 CHECK_READY();
1031
1032 HRESULT rc = checkUSBProxyService();
1033 CheckComRCReturnRC (rc);
1034
1035 if (!mUSBDeviceFilters.size())
1036 return setError (E_INVALIDARG,
1037 tr ("The USB device filter list is empty"));
1038
1039 if (aPosition >= mUSBDeviceFilters.size())
1040 return setError (E_INVALIDARG,
1041 tr ("Invalid position: %lu (must be in range [0, %lu])"),
1042 aPosition, mUSBDeviceFilters.size() - 1);
1043
1044 ComObjPtr <HostUSBDeviceFilter> filter;
1045 {
1046 /* iterate to the position... */
1047 USBDeviceFilterList::iterator it = mUSBDeviceFilters.begin();
1048 std::advance (it, aPosition);
1049 /* ...get an element from there... */
1050 filter = *it;
1051 /* ...and remove */
1052 filter->mInList = false;
1053 mUSBDeviceFilters.erase (it);
1054 }
1055
1056 filter.queryInterfaceTo (aFilter);
1057
1058 /* notify the proxy (only when the filter is active) */
1059 if (filter->data().mActive)
1060 {
1061 ComAssertRet (filter->id() != NULL, E_FAIL);
1062 mUSBProxyService->removeFilter (filter->id());
1063 filter->id() = NULL;
1064 }
1065
1066 /* save the global settings */
1067 alock.unlock();
1068 return mParent->saveSettings();
1069#else
1070 /* Note: The GUI depends on this method returning E_NOTIMPL with no
1071 * extended error info to indicate that USB is simply not available
1072 * (w/o treting it as a failure), for example, as in OSE */
1073 return E_NOTIMPL;
1074#endif
1075}
1076
1077// public methods only for internal purposes
1078////////////////////////////////////////////////////////////////////////////////
1079
1080/**
1081 * Called by setter methods of all USB device filters.
1082 */
1083HRESULT Host::onUSBDeviceFilterChange (HostUSBDeviceFilter *aFilter,
1084 BOOL aActiveChanged /* = FALSE */)
1085{
1086 AutoLock alock (this);
1087 CHECK_READY();
1088
1089 if (aFilter->mInList)
1090 {
1091 if (aActiveChanged)
1092 {
1093 // insert/remove the filter from the proxy
1094 if (aFilter->data().mActive)
1095 {
1096 ComAssertRet (aFilter->id() == NULL, E_FAIL);
1097#ifndef VBOX_WITH_USBFILTER
1098 aFilter->id() =
1099 mUSBProxyService->insertFilter (ComPtr <IUSBDeviceFilter> (aFilter));
1100#else
1101 aFilter->id() = mUSBProxyService->insertFilter (&aFilter->data().mUSBFilter);
1102#endif
1103 }
1104 else
1105 {
1106 ComAssertRet (aFilter->id() != NULL, E_FAIL);
1107 mUSBProxyService->removeFilter (aFilter->id());
1108 aFilter->id() = NULL;
1109 }
1110 }
1111 else
1112 {
1113 if (aFilter->data().mActive)
1114 {
1115 // update the filter in the proxy
1116 ComAssertRet (aFilter->id() != NULL, E_FAIL);
1117 mUSBProxyService->removeFilter (aFilter->id());
1118#ifndef VBOX_WITH_USBFILTER
1119 aFilter->id() =
1120 mUSBProxyService->insertFilter (ComPtr <IUSBDeviceFilter> (aFilter));
1121#else
1122 aFilter->id() = mUSBProxyService->insertFilter (&aFilter->data().mUSBFilter);
1123#endif
1124 }
1125 }
1126
1127 // save the global settings... yeah, on every single filter property change
1128 alock.unlock();
1129 return mParent->saveSettings();
1130 }
1131
1132 return S_OK;
1133}
1134
1135HRESULT Host::loadSettings (const settings::Key &aGlobal)
1136{
1137 using namespace settings;
1138
1139 AutoLock lock (this);
1140 CHECK_READY();
1141
1142 AssertReturn (!aGlobal.isNull(), E_FAIL);
1143
1144 HRESULT rc = S_OK;
1145
1146 Key::List filters = aGlobal.key ("USBDeviceFilters").keys ("DeviceFilter");
1147 for (Key::List::const_iterator it = filters.begin();
1148 it != filters.end(); ++ it)
1149 {
1150 Bstr name = (*it).stringValue ("name");
1151 bool active = (*it).value <bool> ("active");
1152
1153 Bstr vendorId = (*it).stringValue ("vendorid");
1154 Bstr productId = (*it).stringValue ("productid");
1155 Bstr revision = (*it).stringValue ("revision");
1156 Bstr manufacturer = (*it).stringValue ("manufacturer");
1157 Bstr product = (*it).stringValue ("product");
1158 Bstr serialNumber = (*it).stringValue ("serialnumber");
1159 Bstr port = (*it).stringValue ("port");
1160
1161 USBDeviceFilterAction_T action;
1162 action = USBDeviceFilterAction_USBDeviceFilterIgnore;
1163 const char *actionStr = (*it).stringValue ("action");
1164 if (strcmp (actionStr, "Ignore") == 0)
1165 action = USBDeviceFilterAction_USBDeviceFilterIgnore;
1166 else
1167 if (strcmp (actionStr, "Hold") == 0)
1168 action = USBDeviceFilterAction_USBDeviceFilterHold;
1169 else
1170 AssertMsgFailed (("Invalid action: '%s'\n", actionStr));
1171
1172 ComObjPtr <HostUSBDeviceFilter> filterObj;
1173 filterObj.createObject();
1174 rc = filterObj->init (this,
1175 name, active, vendorId, productId, revision,
1176 manufacturer, product, serialNumber, port,
1177 action);
1178 /* error info is set by init() when appropriate */
1179 CheckComRCBreakRC (rc);
1180
1181 mUSBDeviceFilters.push_back (filterObj);
1182 filterObj->mInList = true;
1183
1184 /* notify the proxy (only when the filter is active) */
1185 if (filterObj->data().mActive)
1186 {
1187 HostUSBDeviceFilter *flt = filterObj; /* resolve ambiguity */
1188#ifndef VBOX_WITH_USBFILTER
1189 flt->id() =
1190 mUSBProxyService->insertFilter (ComPtr <IUSBDeviceFilter> (flt));
1191#else
1192 flt->id() = mUSBProxyService->insertFilter (&filterObj->data().mUSBFilter);
1193#endif
1194 }
1195 }
1196
1197 return rc;
1198}
1199
1200HRESULT Host::saveSettings (settings::Key &aGlobal)
1201{
1202 using namespace settings;
1203
1204 AutoLock lock (this);
1205 CHECK_READY();
1206
1207 ComAssertRet (!aGlobal.isNull(), E_FAIL);
1208
1209 /* first, delete the entry */
1210 Key filters = aGlobal.findKey ("USBDeviceFilters");
1211 if (!filters.isNull())
1212 filters.zap();
1213 /* then, recreate it */
1214 filters = aGlobal.createKey ("USBDeviceFilters");
1215
1216 USBDeviceFilterList::const_iterator it = mUSBDeviceFilters.begin();
1217 while (it != mUSBDeviceFilters.end())
1218 {
1219 AutoLock filterLock (*it);
1220 const HostUSBDeviceFilter::Data &data = (*it)->data();
1221
1222 Key filter = filters.appendKey ("DeviceFilter");
1223
1224 filter.setValue <Bstr> ("name", data.mName);
1225 filter.setValue <bool> ("active", !!data.mActive);
1226
1227#ifndef VBOX_WITH_USBFILTER
1228
1229 /* all are optional */
1230 if (data.mVendorId.string())
1231 filter.setValue <Bstr> ("vendorid", data.mVendorId.string());
1232 if (data.mProductId.string())
1233 filter.setValue <Bstr> ("productid", data.mProductId.string());
1234 if (data.mRevision.string())
1235 filter.setValue <Bstr> ("revision", data.mRevision.string());
1236 if (data.mManufacturer.string())
1237 filter.setValue <Bstr> ("manufacturer", data.mManufacturer.string());
1238 if (data.mProduct.string())
1239 filter.setValue <Bstr> ("product", data.mProduct.string());
1240 if (data.mSerialNumber.string())
1241 filter.setValue <Bstr> ("serialnumber", data.mSerialNumber.string());
1242 if (data.mPort.string())
1243 filter.setValue <Bstr> ("port", data.mPort.string());
1244
1245 /* action is mandatory */
1246 if (data.mAction == USBDeviceFilterAction_USBDeviceFilterIgnore)
1247 filter.setStringValue ("action", "Ignore");
1248 else
1249 if (data.mAction == USBDeviceFilterAction_USBDeviceFilterHold)
1250 filter.setStringValue ("action", "Hold");
1251 else
1252 AssertMsgFailed (("Invalid action: %d\n", data.mAction));
1253
1254#else /* VBOX_WITH_USBFILTER */
1255
1256 /* all are optional */
1257 Bstr str;
1258 (*it)->COMGETTER (VendorId) (str.asOutParam());
1259 if (!str.isNull())
1260 filter.setValue <Bstr> ("vendorid", str);
1261
1262 (*it)->COMGETTER (ProductId) (str.asOutParam());
1263 if (!str.isNull())
1264 filter.setValue <Bstr> ("productid", str);
1265
1266 (*it)->COMGETTER (Revision) (str.asOutParam());
1267 if (!str.isNull())
1268 filter.setValue <Bstr> ("revision", str);
1269
1270 (*it)->COMGETTER (Manufacturer) (str.asOutParam());
1271 if (!str.isNull())
1272 filter.setValue <Bstr> ("manufacturer", str);
1273
1274 (*it)->COMGETTER (Product) (str.asOutParam());
1275 if (!str.isNull())
1276 filter.setValue <Bstr> ("product", str);
1277
1278 (*it)->COMGETTER (SerialNumber) (str.asOutParam());
1279 if (!str.isNull())
1280 filter.setValue <Bstr> ("serialnumber", str);
1281
1282 (*it)->COMGETTER (Port) (str.asOutParam());
1283 if (!str.isNull())
1284 filter.setValue <Bstr> ("port", str);
1285
1286 /* action is mandatory */
1287 ULONG action = USBDeviceFilterAction_InvalidUSBDeviceFilterAction;
1288 (*it)->COMGETTER (Action) (&action);
1289 if (action == USBDeviceFilterAction_USBDeviceFilterIgnore)
1290 filter.setStringValue ("action", "Ignore");
1291 else if (action == USBDeviceFilterAction_USBDeviceFilterHold)
1292 filter.setStringValue ("action", "Hold");
1293 else
1294 AssertMsgFailed (("Invalid action: %d\n", action));
1295
1296#endif /* VBOX_WITH_USBFILTER */
1297
1298 ++ it;
1299 }
1300
1301 return S_OK;
1302}
1303
1304/**
1305 * Requests the USB proxy service to capture the given host USB device.
1306 *
1307 * When the request is completed,
1308 * IInternalSessionControl::onUSBDeviceAttach() will be called on the given
1309 * machine object.
1310 *
1311 * Called by Console from the VM process (throug IInternalMachineControl).
1312 * Must return extended error info in case of errors.
1313 */
1314HRESULT Host::captureUSBDevice (SessionMachine *aMachine, INPTR GUIDPARAM aId)
1315{
1316 ComAssertRet (aMachine, E_INVALIDARG);
1317
1318 AutoLock lock (this);
1319 CHECK_READY();
1320
1321 Guid id (aId);
1322
1323 ComObjPtr <HostUSBDevice> device;
1324 USBDeviceList::iterator it = mUSBDevices.begin();
1325 while (!device && it != mUSBDevices.end())
1326 {
1327 if ((*it)->id() == id)
1328 device = (*it);
1329 ++ it;
1330 }
1331
1332 if (!device)
1333 return setError (E_INVALIDARG,
1334 tr ("USB device with UUID {%Vuuid} is not currently attached to the host"),
1335 id.raw());
1336
1337 AutoLock devLock (device);
1338
1339 if (device->isStatePending())
1340 return setError (E_INVALIDARG,
1341 tr ("USB device '%s' with UUID {%Vuuid} is busy (waiting for a pending "
1342 "state change). Please try later"),
1343 device->name().raw(), id.raw());
1344
1345 if (device->state() == USBDeviceState_USBDeviceNotSupported)
1346 return setError (E_INVALIDARG,
1347 tr ("USB device '%s' with UUID {%Vuuid} cannot be accessed by guest "
1348 "computers"),
1349 device->name().raw(), id.raw());
1350
1351 if (device->state() == USBDeviceState_USBDeviceUnavailable)
1352 return setError (E_INVALIDARG,
1353 tr ("USB device '%s' with UUID {%Vuuid} is being exclusively used by the "
1354 "host computer"),
1355 device->name().raw(), id.raw());
1356
1357 if (device->state() == USBDeviceState_USBDeviceCaptured)
1358 {
1359 /* Machine::name() requires a read lock */
1360 AutoReaderLock machLock (device->machine());
1361
1362 return setError (E_INVALIDARG,
1363 tr ("USB device '%s' with UUID {%Vuuid} is already captured by the virtual "
1364 "machine '%ls'"),
1365 device->name().raw(), id.raw(),
1366 device->machine()->name().raw());
1367 }
1368
1369 /* try to capture the device */
1370 device->requestCapture (aMachine);
1371
1372 return S_OK;
1373}
1374
1375/**
1376 * Notification from the VM process that it is going to detach (\a aDone = false)
1377 * or that is has just detach (\a aDone = true) the given USB device.
1378 *
1379 * When \a aDone = false we only inform the USB Proxy about what the vm is
1380 * up to so it doesn't get confused and create a new USB host device object
1381 * (a Darwin issue).
1382 *
1383 * When \a aDone = true we replay all filters against the given USB device
1384 * excluding filters of the machine the device is currently marked as
1385 * captured by.
1386 *
1387 * When the \a aDone = true request is completed,
1388 * IInternalSessionControl::onUSBDeviceDetach() will be called on the given
1389 * machine object.
1390 *
1391 * Called by Console from the VM process (throug IInternalMachineControl).
1392 *
1393 */
1394HRESULT Host::detachUSBDevice (SessionMachine *aMachine, INPTR GUIDPARAM aId, BOOL aDone)
1395{
1396 LogFlowThisFunc (("aMachine=%p, aId={%Vuuid}\n", aMachine, Guid (aId).raw()));
1397
1398 AutoLock lock (this);
1399 CHECK_READY();
1400
1401 ComObjPtr <HostUSBDevice> device;
1402 USBDeviceList::iterator it = mUSBDevices.begin();
1403 while (!device && it != mUSBDevices.end())
1404 {
1405 if ((*it)->id() == aId)
1406 device = (*it);
1407 ++ it;
1408 }
1409
1410 ComAssertRet (!!device, E_FAIL);
1411
1412 AutoLock devLock (device);
1413
1414 LogFlowThisFunc (("id={%Vuuid} state=%d isStatePending=%RTbool pendingState=%d aDone=%RTbool\n",
1415 device->id().raw(), device->state(), device->isStatePending(),
1416 device->pendingState(), aDone));
1417 HRESULT rc = S_OK;
1418 if (!aDone)
1419 {
1420 if (device->isStatePending())
1421 rc = setError (E_INVALIDARG,
1422 tr ("USB device '%s' with UUID {%Vuuid} is busy (waiting for a pending "
1423 "state change). Please try later"),
1424 device->name().raw(), device->id().raw());
1425 else
1426 mUSBProxyService->detachingDevice (device);
1427 }
1428 else
1429 {
1430 if (device->isStatePending())
1431 {
1432 /* If an async detach operation is still pending (darwin), postpone
1433 the setHeld() + the re-applying of filters until it is completed.
1434 We indicate this by moving to the '*Filters' state variant. */
1435 if (device->pendingStateEx() == HostUSBDevice::kDetachingPendingAttach)
1436 device->setLogicalReconnect (HostUSBDevice::kDetachingPendingAttachFilters);
1437 else if (device->pendingStateEx() == HostUSBDevice::kDetachingPendingDetach)
1438 device->setLogicalReconnect (HostUSBDevice::kDetachingPendingDetachFilters);
1439 else
1440 {
1441 Assert (device->pendingStateEx() == HostUSBDevice::kNothingPending);
1442 rc = setError (E_INVALIDARG,
1443 tr ("USB device '%s' with UUID {%Vuuid} is busy (waiting for a pending "
1444 "state change). Please try later"),
1445 device->name().raw(), device->id().raw());
1446 }
1447 }
1448 else
1449 {
1450 ComAssertRet (device->machine() == aMachine, E_FAIL);
1451
1452 /* re-apply filters on the device before giving it back to the host */
1453 device->setHeld();
1454 rc = applyAllUSBFilters (device, aMachine);
1455 ComAssertComRC (rc);
1456 }
1457 }
1458
1459 return rc;
1460}
1461
1462/**
1463 * Asks the USB proxy service to capture all currently available USB devices
1464 * that match filters of the given machine.
1465 *
1466 * When the request is completed,
1467 * IInternalSessionControl::onUSBDeviceDetach() will be called on the given
1468 * machine object per every captured USB device.
1469 *
1470 * Called by Console from the VM process (through IInternalMachineControl)
1471 * upon VM startup.
1472 *
1473 * @note Locks this object for reading (@todo for writing now, until switched
1474 * to the new locking scheme).
1475 */
1476HRESULT Host::autoCaptureUSBDevices (SessionMachine *aMachine)
1477{
1478 LogFlowThisFunc (("aMachine=%p\n", aMachine));
1479
1480 AutoLock lock (this);
1481 CHECK_READY();
1482
1483 for (USBDeviceList::iterator it = mUSBDevices.begin();
1484 it != mUSBDevices.end();
1485 ++ it)
1486 {
1487 ComObjPtr <HostUSBDevice> device = *it;
1488
1489 AutoLock devLock (device);
1490
1491 /* skip pending devices */
1492 if (device->isStatePending())
1493 continue;
1494
1495 if (device->state() == USBDeviceState_USBDeviceBusy ||
1496 device->state() == USBDeviceState_USBDeviceAvailable ||
1497 device->state() == USBDeviceState_USBDeviceHeld)
1498 {
1499 applyMachineUSBFilters (aMachine, device);
1500 }
1501 }
1502
1503 return S_OK;
1504}
1505
1506/**
1507 * Replays all filters against all USB devices currently marked as captured
1508 * by the given machine (excluding this machine's filters).
1509 *
1510 * Called by Console from the VM process (throug IInternalMachineControl)
1511 * upon normal VM termination or by SessionMachine::uninit() upon abnormal
1512 * VM termination (from under the Machine/SessionMachine lock).
1513 *
1514 * @note Locks this object for reading (@todo for writing now, until switched
1515 * to the new locking scheme).
1516 */
1517HRESULT Host::detachAllUSBDevices (SessionMachine *aMachine, BOOL aDone)
1518{
1519 AutoLock lock (this);
1520 CHECK_READY();
1521
1522 USBDeviceList::iterator it = mUSBDevices.begin();
1523 while (it != mUSBDevices.end())
1524 {
1525 ComObjPtr <HostUSBDevice> device = *it;
1526
1527 AutoLock devLock (device);
1528
1529 if (device->machine() == aMachine)
1530 {
1531 if (!aDone)
1532 {
1533 if (!device->isStatePending())
1534 mUSBProxyService->detachingDevice (device);
1535 }
1536 else
1537 {
1538 if (!device->isStatePending())
1539 {
1540 Assert (device->state() == USBDeviceState_USBDeviceCaptured);
1541
1542 /* re-apply filters on the device before giving it back to the
1543 * host */
1544 device->setHeld();
1545 HRESULT rc = applyAllUSBFilters (device, aMachine);
1546 AssertComRC (rc);
1547 }
1548 else if (device->pendingStateEx() == HostUSBDevice::kNothingPending)
1549 device->cancelPendingState();
1550 }
1551 }
1552 ++ it;
1553 }
1554
1555 return S_OK;
1556}
1557
1558// private methods
1559////////////////////////////////////////////////////////////////////////////////
1560
1561#if defined(RT_OS_LINUX) || defined(RT_OS_SOLARIS)
1562# ifdef VBOX_USE_LIBHAL
1563/**
1564 * Helper function to query the hal subsystem for information about DVD drives attached to the
1565 * system.
1566 *
1567 * @returns true if information was successfully obtained, false otherwise
1568 * @retval list drives found will be attached to this list
1569 */
1570bool Host::getDVDInfoFromHal(std::list <ComObjPtr <HostDVDDrive> > &list)
1571{
1572 bool halSuccess = false;
1573 DBusError dbusError;
1574 if (!gLibHalCheckPresence())
1575 return false;
1576 gDBusErrorInit (&dbusError);
1577 DBusConnection *dbusConnection = gDBusBusGet(DBUS_BUS_SYSTEM, &dbusError);
1578 if (dbusConnection != 0)
1579 {
1580 LibHalContext *halContext = gLibHalCtxNew();
1581 if (halContext != 0)
1582 {
1583 if (gLibHalCtxSetDBusConnection (halContext, dbusConnection))
1584 {
1585 if (gLibHalCtxInit(halContext, &dbusError))
1586 {
1587 int numDevices;
1588 char **halDevices = gLibHalFindDeviceStringMatch(halContext,
1589 "storage.drive_type", "cdrom",
1590 &numDevices, &dbusError);
1591 if (halDevices != 0)
1592 {
1593 /* Hal is installed and working, so if no devices are reported, assume
1594 that there are none. */
1595 halSuccess = true;
1596 for (int i = 0; i < numDevices; i++)
1597 {
1598 char *devNode = gLibHalDeviceGetPropertyString(halContext,
1599 halDevices[i], "block.device", &dbusError);
1600#ifdef RT_OS_SOLARIS
1601 /* The CD/DVD ioctls work only for raw device nodes. */
1602 char *tmp = getfullrawname(devNode);
1603 gLibHalFreeString(devNode);
1604 devNode = tmp;
1605#endif
1606 if (devNode != 0)
1607 {
1608// if (validateDevice(devNode, true))
1609// {
1610 Utf8Str description;
1611 char *vendor, *product;
1612 /* We do not check the error here, as this field may
1613 not even exist. */
1614 vendor = gLibHalDeviceGetPropertyString(halContext,
1615 halDevices[i], "info.vendor", 0);
1616 product = gLibHalDeviceGetPropertyString(halContext,
1617 halDevices[i], "info.product", &dbusError);
1618 if ((product != 0 && product[0] != 0))
1619 {
1620 if ((vendor != 0) && (vendor[0] != 0))
1621 {
1622 description = Utf8StrFmt ("%s %s",
1623 vendor, product);
1624 }
1625 else
1626 {
1627 description = product;
1628 }
1629 ComObjPtr <HostDVDDrive> hostDVDDriveObj;
1630 hostDVDDriveObj.createObject();
1631 hostDVDDriveObj->init (Bstr (devNode),
1632 Bstr (halDevices[i]),
1633 Bstr (description));
1634 list.push_back (hostDVDDriveObj);
1635 }
1636 else
1637 {
1638 if (product == 0)
1639 {
1640 LogRel(("Host::COMGETTER(DVDDrives): failed to get property \"info.product\" for device %s. dbus error: %s (%s)\n",
1641 halDevices[i], dbusError.name, dbusError.message));
1642 gDBusErrorFree(&dbusError);
1643 }
1644 ComObjPtr <HostDVDDrive> hostDVDDriveObj;
1645 hostDVDDriveObj.createObject();
1646 hostDVDDriveObj->init (Bstr (devNode),
1647 Bstr (halDevices[i]));
1648 list.push_back (hostDVDDriveObj);
1649 }
1650 if (vendor != 0)
1651 {
1652 gLibHalFreeString(vendor);
1653 }
1654 if (product != 0)
1655 {
1656 gLibHalFreeString(product);
1657 }
1658// }
1659// else
1660// {
1661// LogRel(("Host::COMGETTER(DVDDrives): failed to validate the block device %s as a DVD drive\n"));
1662// }
1663#ifndef RT_OS_SOLARIS
1664 gLibHalFreeString(devNode);
1665#else
1666 free(devNode);
1667#endif
1668 }
1669 else
1670 {
1671 LogRel(("Host::COMGETTER(DVDDrives): failed to get property \"block.device\" for device %s. dbus error: %s (%s)\n",
1672 halDevices[i], dbusError.name, dbusError.message));
1673 gDBusErrorFree(&dbusError);
1674 }
1675 }
1676 gLibHalFreeStringArray(halDevices);
1677 }
1678 else
1679 {
1680 LogRel(("Host::COMGETTER(DVDDrives): failed to get devices with capability \"storage.cdrom\". dbus error: %s (%s)\n", dbusError.name, dbusError.message));
1681 gDBusErrorFree(&dbusError);
1682 }
1683 if (!gLibHalCtxShutdown(halContext, &dbusError)) /* what now? */
1684 {
1685 LogRel(("Host::COMGETTER(DVDDrives): failed to shutdown the libhal context. dbus error: %s (%s)\n", dbusError.name, dbusError.message));
1686 gDBusErrorFree(&dbusError);
1687 }
1688 }
1689 else
1690 {
1691 LogRel(("Host::COMGETTER(DVDDrives): failed to initialise libhal context. dbus error: %s (%s)\n", dbusError.name, dbusError.message));
1692 gDBusErrorFree(&dbusError);
1693 }
1694 gLibHalCtxFree(halContext);
1695 }
1696 else
1697 {
1698 LogRel(("Host::COMGETTER(DVDDrives): failed to set libhal connection to dbus.\n"));
1699 }
1700 }
1701 else
1702 {
1703 LogRel(("Host::COMGETTER(DVDDrives): failed to get a libhal context - out of memory?\n"));
1704 }
1705 gDBusConnectionUnref(dbusConnection);
1706 }
1707 else
1708 {
1709 LogRel(("Host::COMGETTER(DVDDrives): failed to connect to dbus. dbus error: %s (%s)\n", dbusError.name, dbusError.message));
1710 gDBusErrorFree(&dbusError);
1711 }
1712 return halSuccess;
1713}
1714
1715
1716/**
1717 * Helper function to query the hal subsystem for information about floppy drives attached to the
1718 * system.
1719 *
1720 * @returns true if information was successfully obtained, false otherwise
1721 * @retval list drives found will be attached to this list
1722 */
1723bool Host::getFloppyInfoFromHal(std::list <ComObjPtr <HostFloppyDrive> > &list)
1724{
1725 bool halSuccess = false;
1726 DBusError dbusError;
1727 if (!gLibHalCheckPresence())
1728 return false;
1729 gDBusErrorInit (&dbusError);
1730 DBusConnection *dbusConnection = gDBusBusGet(DBUS_BUS_SYSTEM, &dbusError);
1731 if (dbusConnection != 0)
1732 {
1733 LibHalContext *halContext = gLibHalCtxNew();
1734 if (halContext != 0)
1735 {
1736 if (gLibHalCtxSetDBusConnection (halContext, dbusConnection))
1737 {
1738 if (gLibHalCtxInit(halContext, &dbusError))
1739 {
1740 int numDevices;
1741 char **halDevices = gLibHalFindDeviceStringMatch(halContext,
1742 "storage.drive_type", "floppy",
1743 &numDevices, &dbusError);
1744 if (halDevices != 0)
1745 {
1746 /* Hal is installed and working, so if no devices are reported, assume
1747 that there are none. */
1748 halSuccess = true;
1749 for (int i = 0; i < numDevices; i++)
1750 {
1751 char *driveType = gLibHalDeviceGetPropertyString(halContext,
1752 halDevices[i], "storage.drive_type", 0);
1753 if (driveType != 0)
1754 {
1755 if (strcmp(driveType, "floppy") != 0)
1756 {
1757 gLibHalFreeString(driveType);
1758 continue;
1759 }
1760 gLibHalFreeString(driveType);
1761 }
1762 else
1763 {
1764 /* An error occurred. The attribute "storage.drive_type"
1765 probably didn't exist. */
1766 continue;
1767 }
1768 char *devNode = gLibHalDeviceGetPropertyString(halContext,
1769 halDevices[i], "block.device", &dbusError);
1770 if (devNode != 0)
1771 {
1772// if (validateDevice(devNode, false))
1773// {
1774 Utf8Str description;
1775 char *vendor, *product;
1776 /* We do not check the error here, as this field may
1777 not even exist. */
1778 vendor = gLibHalDeviceGetPropertyString(halContext,
1779 halDevices[i], "info.vendor", 0);
1780 product = gLibHalDeviceGetPropertyString(halContext,
1781 halDevices[i], "info.product", &dbusError);
1782 if ((product != 0) && (product[0] != 0))
1783 {
1784 if ((vendor != 0) && (vendor[0] != 0))
1785 {
1786 description = Utf8StrFmt ("%s %s",
1787 vendor, product);
1788 }
1789 else
1790 {
1791 description = product;
1792 }
1793 ComObjPtr <HostFloppyDrive> hostFloppyDrive;
1794 hostFloppyDrive.createObject();
1795 hostFloppyDrive->init (Bstr (devNode),
1796 Bstr (halDevices[i]),
1797 Bstr (description));
1798 list.push_back (hostFloppyDrive);
1799 }
1800 else
1801 {
1802 if (product == 0)
1803 {
1804 LogRel(("Host::COMGETTER(FloppyDrives): failed to get property \"info.product\" for device %s. dbus error: %s (%s)\n",
1805 halDevices[i], dbusError.name, dbusError.message));
1806 gDBusErrorFree(&dbusError);
1807 }
1808 ComObjPtr <HostFloppyDrive> hostFloppyDrive;
1809 hostFloppyDrive.createObject();
1810 hostFloppyDrive->init (Bstr (devNode),
1811 Bstr (halDevices[i]));
1812 list.push_back (hostFloppyDrive);
1813 }
1814 if (vendor != 0)
1815 {
1816 gLibHalFreeString(vendor);
1817 }
1818 if (product != 0)
1819 {
1820 gLibHalFreeString(product);
1821 }
1822// }
1823// else
1824// {
1825// LogRel(("Host::COMGETTER(FloppyDrives): failed to validate the block device %s as a floppy drive\n"));
1826// }
1827 gLibHalFreeString(devNode);
1828 }
1829 else
1830 {
1831 LogRel(("Host::COMGETTER(FloppyDrives): failed to get property \"block.device\" for device %s. dbus error: %s (%s)\n",
1832 halDevices[i], dbusError.name, dbusError.message));
1833 gDBusErrorFree(&dbusError);
1834 }
1835 }
1836 gLibHalFreeStringArray(halDevices);
1837 }
1838 else
1839 {
1840 LogRel(("Host::COMGETTER(FloppyDrives): failed to get devices with capability \"storage.cdrom\". dbus error: %s (%s)\n", dbusError.name, dbusError.message));
1841 gDBusErrorFree(&dbusError);
1842 }
1843 if (!gLibHalCtxShutdown(halContext, &dbusError)) /* what now? */
1844 {
1845 LogRel(("Host::COMGETTER(FloppyDrives): failed to shutdown the libhal context. dbus error: %s (%s)\n", dbusError.name, dbusError.message));
1846 gDBusErrorFree(&dbusError);
1847 }
1848 }
1849 else
1850 {
1851 LogRel(("Host::COMGETTER(FloppyDrives): failed to initialise libhal context. dbus error: %s (%s)\n", dbusError.name, dbusError.message));
1852 gDBusErrorFree(&dbusError);
1853 }
1854 gLibHalCtxFree(halContext);
1855 }
1856 else
1857 {
1858 LogRel(("Host::COMGETTER(FloppyDrives): failed to set libhal connection to dbus.\n"));
1859 }
1860 }
1861 else
1862 {
1863 LogRel(("Host::COMGETTER(FloppyDrives): failed to get a libhal context - out of memory?\n"));
1864 }
1865 gDBusConnectionUnref(dbusConnection);
1866 }
1867 else
1868 {
1869 LogRel(("Host::COMGETTER(FloppyDrives): failed to connect to dbus. dbus error: %s (%s)\n", dbusError.name, dbusError.message));
1870 gDBusErrorFree(&dbusError);
1871 }
1872 return halSuccess;
1873}
1874# endif /* VBOX_USE_HAL defined */
1875
1876/**
1877 * Helper function to parse the given mount file and add found entries
1878 */
1879void Host::parseMountTable(char *mountTable, std::list <ComObjPtr <HostDVDDrive> > &list)
1880{
1881#ifdef RT_OS_LINUX
1882 FILE *mtab = setmntent(mountTable, "r");
1883 if (mtab)
1884 {
1885 struct mntent *mntent;
1886 char *mnt_type;
1887 char *mnt_dev;
1888 char *tmp;
1889 while ((mntent = getmntent(mtab)))
1890 {
1891 mnt_type = (char*)malloc(strlen(mntent->mnt_type) + 1);
1892 mnt_dev = (char*)malloc(strlen(mntent->mnt_fsname) + 1);
1893 strcpy(mnt_type, mntent->mnt_type);
1894 strcpy(mnt_dev, mntent->mnt_fsname);
1895 // supermount fs case
1896 if (strcmp(mnt_type, "supermount") == 0)
1897 {
1898 tmp = strstr(mntent->mnt_opts, "fs=");
1899 if (tmp)
1900 {
1901 free(mnt_type);
1902 mnt_type = strdup(tmp + strlen("fs="));
1903 if (mnt_type)
1904 {
1905 tmp = strchr(mnt_type, ',');
1906 if (tmp)
1907 *tmp = '\0';
1908 }
1909 }
1910 tmp = strstr(mntent->mnt_opts, "dev=");
1911 if (tmp)
1912 {
1913 free(mnt_dev);
1914 mnt_dev = strdup(tmp + strlen("dev="));
1915 if (mnt_dev)
1916 {
1917 tmp = strchr(mnt_dev, ',');
1918 if (tmp)
1919 *tmp = '\0';
1920 }
1921 }
1922 }
1923 // use strstr here to cover things fs types like "udf,iso9660"
1924 if (strstr(mnt_type, "iso9660") == 0)
1925 {
1926 /** @todo check whether we've already got the drive in our list! */
1927 if (validateDevice(mnt_dev, true))
1928 {
1929 ComObjPtr <HostDVDDrive> hostDVDDriveObj;
1930 hostDVDDriveObj.createObject();
1931 hostDVDDriveObj->init (Bstr (mnt_dev));
1932 list.push_back (hostDVDDriveObj);
1933 }
1934 }
1935 free(mnt_dev);
1936 free(mnt_type);
1937 }
1938 endmntent(mtab);
1939 }
1940#else // RT_OS_SOLARIS
1941 FILE *mntFile = fopen(mountTable, "r");
1942 if (mntFile)
1943 {
1944 struct mnttab mntTab;
1945 while (getmntent(mntFile, &mntTab) == 0)
1946 {
1947 char *mountName = strdup(mntTab.mnt_special);
1948 char *mountPoint = strdup(mntTab.mnt_mountp);
1949 char *mountFSType = strdup(mntTab.mnt_fstype);
1950
1951 // skip devices we are not interested in
1952 if ((*mountName && mountName[0] == '/') && // skip 'fake' devices (like -hosts, proc, fd, swap)
1953 (*mountFSType && (strcmp(mountFSType, "devfs") != 0 && // skip devfs (i.e. /devices)
1954 strcmp(mountFSType, "dev") != 0 && // skip dev (i.e. /dev)
1955 strcmp(mountFSType, "lofs") != 0)) && // skip loop-back file-system (lofs)
1956 (*mountPoint && strcmp(mountPoint, "/") != 0)) // skip point '/' (Can CD/DVD be mounted at '/' ???)
1957 {
1958 char *rawDevName = getfullrawname(mountName);
1959 if (validateDevice(rawDevName, true))
1960 {
1961 ComObjPtr <HostDVDDrive> hostDVDDriveObj;
1962 hostDVDDriveObj.createObject();
1963 hostDVDDriveObj->init (Bstr (rawDevName));
1964 list.push_back (hostDVDDriveObj);
1965 }
1966 free(rawDevName);
1967 }
1968
1969 free(mountName);
1970 free(mountPoint);
1971 free(mountFSType);
1972 }
1973
1974 fclose(mntFile);
1975 }
1976#endif
1977}
1978
1979/**
1980 * Helper function to check whether the given device node is a valid drive
1981 */
1982bool Host::validateDevice(const char *deviceNode, bool isCDROM)
1983{
1984 struct stat statInfo;
1985 bool retValue = false;
1986
1987 // sanity check
1988 if (!deviceNode)
1989 {
1990 return false;
1991 }
1992
1993 // first a simple stat() call
1994 if (stat(deviceNode, &statInfo) < 0)
1995 {
1996 return false;
1997 } else
1998 {
1999 if (isCDROM)
2000 {
2001 if (S_ISCHR(statInfo.st_mode) || S_ISBLK(statInfo.st_mode))
2002 {
2003 int fileHandle;
2004 // now try to open the device
2005 fileHandle = open(deviceNode, O_RDONLY | O_NONBLOCK, 0);
2006 if (fileHandle >= 0)
2007 {
2008 cdrom_subchnl cdChannelInfo;
2009 cdChannelInfo.cdsc_format = CDROM_MSF;
2010 // this call will finally reveal the whole truth
2011#ifdef RT_OS_LINUX
2012 if ((ioctl(fileHandle, CDROMSUBCHNL, &cdChannelInfo) == 0) ||
2013 (errno == EIO) || (errno == ENOENT) ||
2014 (errno == EINVAL) || (errno == ENOMEDIUM))
2015#else
2016 if ((ioctl(fileHandle, CDROMSUBCHNL, &cdChannelInfo) == 0) ||
2017 (errno == EIO) || (errno == ENOENT) ||
2018 (errno == EINVAL))
2019#endif
2020 {
2021 retValue = true;
2022 }
2023 close(fileHandle);
2024 }
2025 }
2026 } else
2027 {
2028 // floppy case
2029 if (S_ISCHR(statInfo.st_mode) || S_ISBLK(statInfo.st_mode))
2030 {
2031 /// @todo do some more testing, maybe a nice IOCTL!
2032 retValue = true;
2033 }
2034 }
2035 }
2036 return retValue;
2037}
2038#endif // RT_OS_LINUX || RT_OS_SOLARIS
2039
2040/**
2041 * Applies all (golbal and VM) filters to the given USB device. The device
2042 * must be either a newly attached device or a device released by a VM.
2043 *
2044 * This method will request the USB proxy service to release the device (give
2045 * it back to the host) if none of the global or VM filters want to capture
2046 * the device.
2047 *
2048 * @param aDevice USB device to apply filters to.
2049 * @param aMachine Machine the device was released by or @c NULL.
2050 *
2051 * @note the method must be called from under this object's write lock and
2052 * from the aDevice's write lock.
2053 */
2054HRESULT Host::applyAllUSBFilters (ComObjPtr <HostUSBDevice> &aDevice,
2055 SessionMachine *aMachine /* = NULL */)
2056{
2057 LogFlowThisFunc (("\n"));
2058
2059 /// @todo must check for read lock, it's enough here
2060 AssertReturn (isLockedOnCurrentThread(), E_FAIL);
2061
2062 AssertReturn (aDevice->isLockedOnCurrentThread(), E_FAIL);
2063
2064 AssertReturn (aDevice->state() != USBDeviceState_USBDeviceCaptured, E_FAIL);
2065
2066 AssertReturn (aDevice->isStatePending() == false, E_FAIL);
2067
2068 /* ignore unsupported devices */
2069 if (aDevice->state() == USBDeviceState_USBDeviceNotSupported)
2070 return S_OK;
2071 /* ignore unavailable devices as well */
2072 if (aDevice->state() == USBDeviceState_USBDeviceUnavailable)
2073 return S_OK;
2074
2075 VirtualBox::SessionMachineVector machines;
2076 mParent->getOpenedMachines (machines);
2077
2078 /// @todo it may be better to take a copy of filters to iterate and leave
2079 /// the host lock before calling HostUSBDevice:requestCapture() (which
2080 /// calls the VM process).
2081
2082 /* apply global filters */
2083 USBDeviceFilterList::const_iterator it = mUSBDeviceFilters.begin();
2084 for (; it != mUSBDeviceFilters.end(); ++ it)
2085 {
2086 AutoLock filterLock (*it);
2087 const HostUSBDeviceFilter::Data &data = (*it)->data();
2088 if (aDevice->isMatch (data))
2089 {
2090#ifndef VBOX_WITH_USBFILTER
2091 USBDeviceFilterAction_T action = data.mAction;
2092#else
2093 ULONG action = USBDeviceFilterAction_InvalidUSBDeviceFilterAction;
2094 (*it)->COMGETTER (Action) (&action);
2095#endif
2096 if (action == USBDeviceFilterAction_USBDeviceFilterIgnore)
2097 {
2098 /* request to give the device back to the host*/
2099 aDevice->requestRelease();
2100 /* nothing to do any more */
2101 return S_OK;
2102 }
2103 if (action == USBDeviceFilterAction_USBDeviceFilterHold)
2104 break;
2105 }
2106 }
2107
2108 /* apply machine filters */
2109 size_t i = 0;
2110 for (; i < machines.size(); ++ i)
2111 {
2112 /* skip the machine the device was just detached from */
2113 if (aMachine && machines [i] == aMachine)
2114 continue;
2115
2116 if (applyMachineUSBFilters (machines [i], aDevice))
2117 break;
2118 }
2119
2120 if (i == machines.size())
2121 {
2122 /* no matched machine filters, check what to do */
2123 if (it == mUSBDeviceFilters.end())
2124 {
2125 /* no any filter matched at all */
2126 /* request to give the device back to the host */
2127 aDevice->requestRelease();
2128 }
2129 else
2130 {
2131 /* there was a global Hold filter */
2132 aDevice->requestHold();
2133 }
2134 }
2135
2136 return S_OK;
2137}
2138
2139/**
2140 * Runs through filters of the given machine and asks the USB proxy service
2141 * to capture the given USB device when there is a match.
2142 *
2143 * @param aMachine Machine whose filters are to be run.
2144 * @param aDevice USB device, a candidate for auto-capturing.
2145 * @return @c true if there was a match and @c false otherwise.
2146 *
2147 * @note the method must be called from under this object's write lock and
2148 * from the aDevice's write lock.
2149 *
2150 * @note Locks aMachine for reading.
2151 */
2152bool Host::applyMachineUSBFilters (SessionMachine *aMachine,
2153 ComObjPtr <HostUSBDevice> &aDevice)
2154{
2155 LogFlowThisFunc (("\n"));
2156
2157 AssertReturn (aMachine, false);
2158
2159 /// @todo must check for read lock, it's enough here
2160 AssertReturn (isLockedOnCurrentThread(), false);
2161
2162 AssertReturn (aDevice->isLockedOnCurrentThread(), false);
2163
2164 AssertReturn (aDevice->state() != USBDeviceState_USBDeviceNotSupported, false);
2165 AssertReturn (aDevice->state() != USBDeviceState_USBDeviceUnavailable, false);
2166
2167 AssertReturn (aDevice->isStatePending() == false, false);
2168
2169 ULONG maskedIfs;
2170 bool hasMatch = aMachine->hasMatchingUSBFilter (aDevice, &maskedIfs);
2171
2172 if (hasMatch)
2173 {
2174 /* try to capture the device */
2175 return aDevice->requestCapture (aMachine, maskedIfs);
2176 }
2177
2178 return hasMatch;
2179}
2180
2181/**
2182 * Called by USB proxy service when a new device is physically attached
2183 * to the host.
2184 *
2185 * @param aDevice Pointer to the device which has been attached.
2186 */
2187void Host::onUSBDeviceAttached (HostUSBDevice *aDevice)
2188{
2189 LogFlowThisFunc (("aDevice=%p\n", aDevice));
2190
2191 AssertReturnVoid (aDevice);
2192
2193 AssertReturnVoid (isLockedOnCurrentThread());
2194 AssertReturnVoid (aDevice->isLockedOnCurrentThread());
2195
2196 LogFlowThisFunc (("id={%Vuuid} state=%d isStatePending=%RTbool pendingState=%d\n",
2197 aDevice->id().raw(), aDevice->state(), aDevice->isStatePending(),
2198 aDevice->pendingState()));
2199
2200 Assert (aDevice->isStatePending() == false);
2201
2202 /* add to the collecion */
2203 mUSBDevices.push_back (aDevice);
2204
2205 /* apply all filters */
2206 ComObjPtr <HostUSBDevice> device (aDevice);
2207 HRESULT rc = applyAllUSBFilters (device);
2208 AssertComRC (rc);
2209}
2210
2211/**
2212 * Called by USB proxy service when the device is physically detached
2213 * from the host.
2214 *
2215 * @param aDevice Pointer to the device which has been detached.
2216 */
2217void Host::onUSBDeviceDetached (HostUSBDevice *aDevice)
2218{
2219 LogFlowThisFunc (("aDevice=%p\n", aDevice));
2220
2221 AssertReturnVoid (aDevice);
2222
2223 AssertReturnVoid (isLockedOnCurrentThread());
2224 AssertReturnVoid (aDevice->isLockedOnCurrentThread());
2225
2226 LogFlowThisFunc (("id={%Vuuid} state=%d isStatePending=%RTbool pendingState=%d\n",
2227 aDevice->id().raw(), aDevice->state(), aDevice->isStatePending(),
2228 aDevice->pendingState()));
2229
2230 Guid id = aDevice->id();
2231
2232 ComObjPtr <HostUSBDevice> device;
2233 Host::USBDeviceList::iterator it = mUSBDevices.begin();
2234 while (it != mUSBDevices.end())
2235 {
2236 if ((*it)->id() == id)
2237 {
2238 device = (*it);
2239 break;
2240 }
2241 ++ it;
2242 }
2243
2244 AssertReturnVoid (!!device);
2245
2246 /* remove from the collecion */
2247 mUSBDevices.erase (it);
2248
2249 /* Detach the device from any machine currently using it,
2250 reset all data and uninitialize the device object. */
2251 device->onDetachedPhys();
2252}
2253
2254/**
2255 * Called by USB proxy service when the state of the device has changed
2256 * either because of the state change request or because of some external
2257 * interaction.
2258 *
2259 * @param aDevice The device in question.
2260 */
2261void Host::onUSBDeviceStateChanged (HostUSBDevice *aDevice)
2262{
2263 LogFlowThisFunc (("aDevice=%p\n", aDevice));
2264
2265 AssertReturnVoid (aDevice);
2266
2267 AssertReturnVoid (isLockedOnCurrentThread());
2268 AssertReturnVoid (aDevice->isLockedOnCurrentThread());
2269
2270 LogFlowThisFunc (("id={%Vuuid} state=%d isStatePending=%RTbool pendingState=%d\n",
2271 aDevice->id().raw(), aDevice->state(), aDevice->isStatePending(),
2272 aDevice->pendingState()));
2273
2274
2275 ComObjPtr <HostUSBDevice> device (aDevice);
2276 if (device->isStatePending())
2277 {
2278 /* it was a state change request */
2279 if (device->pendingStateEx() == HostUSBDevice::kDetachingPendingAttachFilters)
2280 {
2281 /* The device has completed an asynchronous detach operation, subject
2282 it to the filters and such if the current state permits this.
2283 (handlePendingStateChange will disassociate itself from the machine.) */
2284 ComObjPtr <SessionMachine> machine (device->machine());
2285 device->handlePendingStateChange();
2286 if (device->state() == USBDeviceState_USBDeviceCaptured)
2287 {
2288 Log (("USB: running filters on async detached device\n"));
2289 device->setHeld();
2290 HRESULT rc = applyAllUSBFilters (device, machine);
2291 AssertComRC (rc);
2292 }
2293 else
2294 Log (("USB: async detached devices reappeared in stated %d instead of %d!\n",
2295 device->state(), USBDeviceState_USBDeviceCaptured));
2296 }
2297 else
2298 device->handlePendingStateChange();
2299 }
2300 else if ( device->state() == USBDeviceState_USBDeviceAvailable
2301 || device->state() == USBDeviceState_USBDeviceBusy)
2302 {
2303 /* The device has gone from being unavailable (not subject to filters) to being
2304 available / busy. This transition can be triggered by udevd or manual
2305 permission changes on Linux. On all systems may be triggered by the host
2306 ceasing to use the device - like unmounting an MSD in the Finder or invoking
2307 the "Safely remove XXXX" stuff on Windows (perhaps). */
2308 HRESULT rc = applyAllUSBFilters (device);
2309 AssertComRC (rc);
2310 }
2311 else
2312 {
2313 /* some external state change */
2314
2315 /// @todo re-run all USB filters probably
2316 AssertFailed();
2317 }
2318}
2319
2320/**
2321 * Checks for the presense and status of the USB Proxy Service.
2322 * Returns S_OK when the Proxy is present and OK, or E_FAIL and a
2323 * corresponding error message otherwise. Intended to be used by methods
2324 * that rely on the Proxy Service availability.
2325 *
2326 * @note Locks this object for reading.
2327 */
2328HRESULT Host::checkUSBProxyService()
2329{
2330#ifdef VBOX_WITH_USB
2331 AutoLock lock (this);
2332 CHECK_READY();
2333
2334 AssertReturn (mUSBProxyService, E_FAIL);
2335 if (!mUSBProxyService->isActive())
2336 {
2337 /* disable the USB controller completely to avoid assertions if the
2338 * USB proxy service could not start. */
2339
2340 Assert (VBOX_FAILURE (mUSBProxyService->getLastError()));
2341 if (mUSBProxyService->getLastError() == VERR_FILE_NOT_FOUND)
2342 return setError (E_FAIL,
2343 tr ("Could not load the Host USB Proxy Service (%Vrc). "
2344 "The service might be not installed on the host computer"),
2345 mUSBProxyService->getLastError());
2346 else
2347 return setError (E_FAIL,
2348 tr ("Could not load the Host USB Proxy service (%Vrc)"),
2349 mUSBProxyService->getLastError());
2350 }
2351
2352 return S_OK;
2353#else
2354 return E_NOTIMPL;
2355#endif
2356}
2357
2358#ifdef RT_OS_WINDOWS
2359
2360/* The original source of the VBoxTAP adapter creation/destruction code has the following copyright */
2361/*
2362 Copyright 2004 by the Massachusetts Institute of Technology
2363
2364 All rights reserved.
2365
2366 Permission to use, copy, modify, and distribute this software and its
2367 documentation for any purpose and without fee is hereby granted,
2368 provided that the above copyright notice appear in all copies and that
2369 both that copyright notice and this permission notice appear in
2370 supporting documentation, and that the name of the Massachusetts
2371 Institute of Technology (M.I.T.) not be used in advertising or publicity
2372 pertaining to distribution of the software without specific, written
2373 prior permission.
2374
2375 M.I.T. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
2376 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
2377 M.I.T. BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
2378 ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
2379 WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
2380 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
2381 SOFTWARE.
2382*/
2383
2384
2385#define NETSHELL_LIBRARY _T("netshell.dll")
2386
2387/**
2388 * Use the IShellFolder API to rename the connection.
2389 */
2390static HRESULT rename_shellfolder (PCWSTR wGuid, PCWSTR wNewName)
2391{
2392 /* This is the GUID for the network connections folder. It is constant.
2393 * {7007ACC7-3202-11D1-AAD2-00805FC1270E} */
2394 const GUID CLSID_NetworkConnections = {
2395 0x7007ACC7, 0x3202, 0x11D1, {
2396 0xAA, 0xD2, 0x00, 0x80, 0x5F, 0xC1, 0x27, 0x0E
2397 }
2398 };
2399
2400 LPITEMIDLIST pidl = NULL;
2401 IShellFolder *pShellFolder = NULL;
2402 HRESULT hr;
2403
2404 /* Build the display name in the form "::{GUID}". */
2405 if (wcslen (wGuid) >= MAX_PATH)
2406 return E_INVALIDARG;
2407 WCHAR szAdapterGuid[MAX_PATH + 2] = {0};
2408 swprintf (szAdapterGuid, L"::%ls", wGuid);
2409
2410 /* Create an instance of the network connections folder. */
2411 hr = CoCreateInstance (CLSID_NetworkConnections, NULL,
2412 CLSCTX_INPROC_SERVER, IID_IShellFolder,
2413 reinterpret_cast <LPVOID *> (&pShellFolder));
2414 /* Parse the display name. */
2415 if (SUCCEEDED (hr))
2416 {
2417 hr = pShellFolder->ParseDisplayName (NULL, NULL, szAdapterGuid, NULL,
2418 &pidl, NULL);
2419 }
2420 if (SUCCEEDED (hr))
2421 {
2422 hr = pShellFolder->SetNameOf (NULL, pidl, wNewName, SHGDN_NORMAL,
2423 &pidl);
2424 }
2425
2426 CoTaskMemFree (pidl);
2427
2428 if (pShellFolder)
2429 pShellFolder->Release();
2430
2431 return hr;
2432}
2433
2434extern "C" HRESULT RenameConnection (PCWSTR GuidString, PCWSTR NewName)
2435{
2436 typedef HRESULT (WINAPI *lpHrRenameConnection) (const GUID *, PCWSTR);
2437 lpHrRenameConnection RenameConnectionFunc = NULL;
2438 HRESULT status;
2439
2440 /* First try the IShellFolder interface, which was unimplemented
2441 * for the network connections folder before XP. */
2442 status = rename_shellfolder (GuidString, NewName);
2443 if (status == E_NOTIMPL)
2444 {
2445/** @todo that code doesn't seem to work! */
2446 /* The IShellFolder interface is not implemented on this platform.
2447 * Try the (undocumented) HrRenameConnection API in the netshell
2448 * library. */
2449 CLSID clsid;
2450 HINSTANCE hNetShell;
2451 status = CLSIDFromString ((LPOLESTR) GuidString, &clsid);
2452 if (FAILED(status))
2453 return E_FAIL;
2454 hNetShell = LoadLibrary (NETSHELL_LIBRARY);
2455 if (hNetShell == NULL)
2456 return E_FAIL;
2457 RenameConnectionFunc =
2458 (lpHrRenameConnection) GetProcAddress (hNetShell,
2459 "HrRenameConnection");
2460 if (RenameConnectionFunc == NULL)
2461 {
2462 FreeLibrary (hNetShell);
2463 return E_FAIL;
2464 }
2465 status = RenameConnectionFunc (&clsid, NewName);
2466 FreeLibrary (hNetShell);
2467 }
2468 if (FAILED (status))
2469 return status;
2470
2471 return S_OK;
2472}
2473
2474#define DRIVERHWID _T("vboxtap")
2475
2476#define SetErrBreak(strAndArgs) \
2477 if (1) { \
2478 aErrMsg = Utf8StrFmt strAndArgs; vrc = VERR_GENERAL_FAILURE; break; \
2479 } else do {} while (0)
2480
2481/* static */
2482int Host::createNetworkInterface (SVCHlpClient *aClient,
2483 const Utf8Str &aName,
2484 Guid &aGUID, Utf8Str &aErrMsg)
2485{
2486 LogFlowFuncEnter();
2487 LogFlowFunc (("Network connection name = '%s'\n", aName.raw()));
2488
2489 AssertReturn (aClient, VERR_INVALID_POINTER);
2490 AssertReturn (!aName.isNull(), VERR_INVALID_PARAMETER);
2491
2492 int vrc = VINF_SUCCESS;
2493
2494 HDEVINFO hDeviceInfo = INVALID_HANDLE_VALUE;
2495 SP_DEVINFO_DATA DeviceInfoData;
2496 DWORD ret = 0;
2497 BOOL found = FALSE;
2498 BOOL registered = FALSE;
2499 BOOL destroyList = FALSE;
2500 TCHAR pCfgGuidString [50];
2501
2502 do
2503 {
2504 BOOL ok;
2505 GUID netGuid;
2506 SP_DRVINFO_DATA DriverInfoData;
2507 SP_DEVINSTALL_PARAMS DeviceInstallParams;
2508 TCHAR className [MAX_PATH];
2509 DWORD index = 0;
2510 PSP_DRVINFO_DETAIL_DATA pDriverInfoDetail;
2511 /* for our purposes, 2k buffer is more
2512 * than enough to obtain the hardware ID
2513 * of the VBoxTAP driver. */
2514 DWORD detailBuf [2048];
2515
2516 HKEY hkey = NULL;
2517 DWORD cbSize;
2518 DWORD dwValueType;
2519
2520 /* initialize the structure size */
2521 DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
2522 DriverInfoData.cbSize = sizeof(SP_DRVINFO_DATA);
2523
2524 /* copy the net class GUID */
2525 memcpy(&netGuid, &GUID_DEVCLASS_NET, sizeof(GUID_DEVCLASS_NET));
2526
2527 /* create an empty device info set associated with the net class GUID */
2528 hDeviceInfo = SetupDiCreateDeviceInfoList (&netGuid, NULL);
2529 if (hDeviceInfo == INVALID_HANDLE_VALUE)
2530 SetErrBreak (("SetupDiCreateDeviceInfoList failed (0x%08X)",
2531 GetLastError()));
2532
2533 /* get the class name from GUID */
2534 ok = SetupDiClassNameFromGuid (&netGuid, className, MAX_PATH, NULL);
2535 if (!ok)
2536 SetErrBreak (("SetupDiClassNameFromGuid failed (0x%08X)",
2537 GetLastError()));
2538
2539 /* create a device info element and add the new device instance
2540 * key to registry */
2541 ok = SetupDiCreateDeviceInfo (hDeviceInfo, className, &netGuid, NULL, NULL,
2542 DICD_GENERATE_ID, &DeviceInfoData);
2543 if (!ok)
2544 SetErrBreak (("SetupDiCreateDeviceInfo failed (0x%08X)",
2545 GetLastError()));
2546
2547 /* select the newly created device info to be the currently
2548 selected member */
2549 ok = SetupDiSetSelectedDevice (hDeviceInfo, &DeviceInfoData);
2550 if (!ok)
2551 SetErrBreak (("SetupDiSetSelectedDevice failed (0x%08X)",
2552 GetLastError()));
2553
2554 /* build a list of class drivers */
2555 ok = SetupDiBuildDriverInfoList (hDeviceInfo, &DeviceInfoData,
2556 SPDIT_CLASSDRIVER);
2557 if (!ok)
2558 SetErrBreak (("SetupDiBuildDriverInfoList failed (0x%08X)",
2559 GetLastError()));
2560
2561 destroyList = TRUE;
2562
2563 /* enumerate the driver info list */
2564 while (TRUE)
2565 {
2566 BOOL ret;
2567
2568 ret = SetupDiEnumDriverInfo (hDeviceInfo, &DeviceInfoData,
2569 SPDIT_CLASSDRIVER, index, &DriverInfoData);
2570
2571 /* if the function failed and GetLastError() returned
2572 * ERROR_NO_MORE_ITEMS, then we have reached the end of the
2573 * list. Othewise there was something wrong with this
2574 * particular driver. */
2575 if (!ret)
2576 {
2577 if(GetLastError() == ERROR_NO_MORE_ITEMS)
2578 break;
2579 else
2580 {
2581 index++;
2582 continue;
2583 }
2584 }
2585
2586 pDriverInfoDetail = (PSP_DRVINFO_DETAIL_DATA) detailBuf;
2587 pDriverInfoDetail->cbSize = sizeof(SP_DRVINFO_DETAIL_DATA);
2588
2589 /* if we successfully find the hardware ID and it turns out to
2590 * be the one for the loopback driver, then we are done. */
2591 if (SetupDiGetDriverInfoDetail (hDeviceInfo,
2592 &DeviceInfoData,
2593 &DriverInfoData,
2594 pDriverInfoDetail,
2595 sizeof (detailBuf),
2596 NULL))
2597 {
2598 TCHAR * t;
2599
2600 /* pDriverInfoDetail->HardwareID is a MULTISZ string. Go through the
2601 * whole list and see if there is a match somewhere. */
2602 t = pDriverInfoDetail->HardwareID;
2603 while (t && *t && t < (TCHAR *) &detailBuf [sizeof(detailBuf) / sizeof (detailBuf[0])])
2604 {
2605 if (!_tcsicmp(t, DRIVERHWID))
2606 break;
2607
2608 t += _tcslen(t) + 1;
2609 }
2610
2611 if (t && *t && t < (TCHAR *) &detailBuf [sizeof(detailBuf) / sizeof (detailBuf[0])])
2612 {
2613 found = TRUE;
2614 break;
2615 }
2616 }
2617
2618 index ++;
2619 }
2620
2621 if (!found)
2622 SetErrBreak ((tr ("Could not find Host Interface Networking driver! "
2623 "Please reinstall")));
2624
2625 /* set the loopback driver to be the currently selected */
2626 ok = SetupDiSetSelectedDriver (hDeviceInfo, &DeviceInfoData,
2627 &DriverInfoData);
2628 if (!ok)
2629 SetErrBreak (("SetupDiSetSelectedDriver failed (0x%08X)",
2630 GetLastError()));
2631
2632 /* register the phantom device to prepare for install */
2633 ok = SetupDiCallClassInstaller (DIF_REGISTERDEVICE, hDeviceInfo,
2634 &DeviceInfoData);
2635 if (!ok)
2636 SetErrBreak (("SetupDiCallClassInstaller failed (0x%08X)",
2637 GetLastError()));
2638
2639 /* registered, but remove if errors occur in the following code */
2640 registered = TRUE;
2641
2642 /* ask the installer if we can install the device */
2643 ok = SetupDiCallClassInstaller (DIF_ALLOW_INSTALL, hDeviceInfo,
2644 &DeviceInfoData);
2645 if (!ok)
2646 {
2647 if (GetLastError() != ERROR_DI_DO_DEFAULT)
2648 SetErrBreak (("SetupDiCallClassInstaller (DIF_ALLOW_INSTALL) failed (0x%08X)",
2649 GetLastError()));
2650 /* that's fine */
2651 }
2652
2653 /* install the files first */
2654 ok = SetupDiCallClassInstaller (DIF_INSTALLDEVICEFILES, hDeviceInfo,
2655 &DeviceInfoData);
2656 if (!ok)
2657 SetErrBreak (("SetupDiCallClassInstaller (DIF_INSTALLDEVICEFILES) failed (0x%08X)",
2658 GetLastError()));
2659
2660 /* get the device install parameters and disable filecopy */
2661 DeviceInstallParams.cbSize = sizeof(SP_DEVINSTALL_PARAMS);
2662 ok = SetupDiGetDeviceInstallParams (hDeviceInfo, &DeviceInfoData,
2663 &DeviceInstallParams);
2664 if (ok)
2665 {
2666 DeviceInstallParams.Flags |= DI_NOFILECOPY;
2667 ok = SetupDiSetDeviceInstallParams (hDeviceInfo, &DeviceInfoData,
2668 &DeviceInstallParams);
2669 if (!ok)
2670 SetErrBreak (("SetupDiSetDeviceInstallParams failed (0x%08X)",
2671 GetLastError()));
2672 }
2673
2674 /*
2675 * Register any device-specific co-installers for this device,
2676 */
2677
2678 ok = SetupDiCallClassInstaller (DIF_REGISTER_COINSTALLERS,
2679 hDeviceInfo,
2680 &DeviceInfoData);
2681 if (!ok)
2682 SetErrBreak (("SetupDiCallClassInstaller (DIF_REGISTER_COINSTALLERS) failed (0x%08X)",
2683 GetLastError()));
2684
2685 /*
2686 * install any installer-specified interfaces.
2687 * and then do the real install
2688 */
2689 ok = SetupDiCallClassInstaller (DIF_INSTALLINTERFACES,
2690 hDeviceInfo,
2691 &DeviceInfoData);
2692 if (!ok)
2693 SetErrBreak (("SetupDiCallClassInstaller (DIF_INSTALLINTERFACES) failed (0x%08X)",
2694 GetLastError()));
2695
2696 ok = SetupDiCallClassInstaller (DIF_INSTALLDEVICE,
2697 hDeviceInfo,
2698 &DeviceInfoData);
2699 if (!ok)
2700 SetErrBreak (("SetupDiCallClassInstaller (DIF_INSTALLDEVICE) failed (0x%08X)",
2701 GetLastError()));
2702
2703 /* Figure out NetCfgInstanceId */
2704 hkey = SetupDiOpenDevRegKey (hDeviceInfo,
2705 &DeviceInfoData,
2706 DICS_FLAG_GLOBAL,
2707 0,
2708 DIREG_DRV,
2709 KEY_READ);
2710 if (hkey == INVALID_HANDLE_VALUE)
2711 SetErrBreak (("SetupDiOpenDevRegKey failed (0x%08X)",
2712 GetLastError()));
2713
2714 cbSize = sizeof (pCfgGuidString);
2715 DWORD ret;
2716 ret = RegQueryValueEx (hkey, _T ("NetCfgInstanceId"), NULL,
2717 &dwValueType, (LPBYTE) pCfgGuidString, &cbSize);
2718 RegCloseKey (hkey);
2719
2720 ret = RenameConnection (pCfgGuidString, Bstr (aName));
2721 if (FAILED (ret))
2722 SetErrBreak (("Failed to set interface name (ret=0x%08X, "
2723 "pCfgGuidString='%ls', cbSize=%d)",
2724 ret, pCfgGuidString, cbSize));
2725 }
2726 while (0);
2727
2728 /*
2729 * cleanup
2730 */
2731
2732 if (hDeviceInfo != INVALID_HANDLE_VALUE)
2733 {
2734 /* an error has occured, but the device is registered, we must remove it */
2735 if (ret != 0 && registered)
2736 SetupDiCallClassInstaller (DIF_REMOVE, hDeviceInfo, &DeviceInfoData);
2737
2738 found = SetupDiDeleteDeviceInfo (hDeviceInfo, &DeviceInfoData);
2739
2740 /* destroy the driver info list */
2741 if (destroyList)
2742 SetupDiDestroyDriverInfoList (hDeviceInfo, &DeviceInfoData,
2743 SPDIT_CLASSDRIVER);
2744 /* clean up the device info set */
2745 SetupDiDestroyDeviceInfoList (hDeviceInfo);
2746 }
2747
2748 /* return the network connection GUID on success */
2749 if (VBOX_SUCCESS (vrc))
2750 {
2751 /* remove the curly bracket at the end */
2752 pCfgGuidString [_tcslen (pCfgGuidString) - 1] = '\0';
2753 LogFlowFunc (("Network connection GUID string = {%ls}\n", pCfgGuidString + 1));
2754
2755 aGUID = Guid (Utf8Str (pCfgGuidString + 1));
2756 LogFlowFunc (("Network connection GUID = {%Vuuid}\n", aGUID.raw()));
2757 Assert (!aGUID.isEmpty());
2758 }
2759
2760 LogFlowFunc (("vrc=%Vrc\n", vrc));
2761 LogFlowFuncLeave();
2762 return vrc;
2763}
2764
2765/* static */
2766int Host::removeNetworkInterface (SVCHlpClient *aClient,
2767 const Guid &aGUID,
2768 Utf8Str &aErrMsg)
2769{
2770 LogFlowFuncEnter();
2771 LogFlowFunc (("Network connection GUID = {%Vuuid}\n", aGUID.raw()));
2772
2773 AssertReturn (aClient, VERR_INVALID_POINTER);
2774 AssertReturn (!aGUID.isEmpty(), VERR_INVALID_PARAMETER);
2775
2776 int vrc = VINF_SUCCESS;
2777
2778 do
2779 {
2780 TCHAR lszPnPInstanceId [512] = {0};
2781
2782 /* We have to find the device instance ID through a registry search */
2783
2784 HKEY hkeyNetwork = 0;
2785 HKEY hkeyConnection = 0;
2786
2787 do
2788 {
2789 char strRegLocation [256];
2790 sprintf (strRegLocation,
2791 "SYSTEM\\CurrentControlSet\\Control\\Network\\"
2792 "{4D36E972-E325-11CE-BFC1-08002BE10318}\\{%s}",
2793 aGUID.toString().raw());
2794 LONG status;
2795 status = RegOpenKeyExA (HKEY_LOCAL_MACHINE, strRegLocation, 0,
2796 KEY_READ, &hkeyNetwork);
2797 if ((status != ERROR_SUCCESS) || !hkeyNetwork)
2798 SetErrBreak ((
2799 tr ("Host interface network is not found in registry (%s) [1]"),
2800 strRegLocation));
2801
2802 status = RegOpenKeyExA (hkeyNetwork, "Connection", 0,
2803 KEY_READ, &hkeyConnection);
2804 if ((status != ERROR_SUCCESS) || !hkeyConnection)
2805 SetErrBreak ((
2806 tr ("Host interface network is not found in registry (%s) [2]"),
2807 strRegLocation));
2808
2809 DWORD len = sizeof (lszPnPInstanceId);
2810 DWORD dwKeyType;
2811 status = RegQueryValueExW (hkeyConnection, L"PnPInstanceID", NULL,
2812 &dwKeyType, (LPBYTE) lszPnPInstanceId, &len);
2813 if ((status != ERROR_SUCCESS) || (dwKeyType != REG_SZ))
2814 SetErrBreak ((
2815 tr ("Host interface network is not found in registry (%s) [3]"),
2816 strRegLocation));
2817 }
2818 while (0);
2819
2820 if (hkeyConnection)
2821 RegCloseKey (hkeyConnection);
2822 if (hkeyNetwork)
2823 RegCloseKey (hkeyNetwork);
2824
2825 if (VBOX_FAILURE (vrc))
2826 break;
2827
2828 /*
2829 * Now we are going to enumerate all network devices and
2830 * wait until we encounter the right device instance ID
2831 */
2832
2833 HDEVINFO hDeviceInfo = INVALID_HANDLE_VALUE;
2834
2835 do
2836 {
2837 BOOL ok;
2838 DWORD ret = 0;
2839 GUID netGuid;
2840 SP_DEVINFO_DATA DeviceInfoData;
2841 DWORD index = 0;
2842 BOOL found = FALSE;
2843 DWORD size = 0;
2844
2845 /* initialize the structure size */
2846 DeviceInfoData.cbSize = sizeof (SP_DEVINFO_DATA);
2847
2848 /* copy the net class GUID */
2849 memcpy (&netGuid, &GUID_DEVCLASS_NET, sizeof (GUID_DEVCLASS_NET));
2850
2851 /* return a device info set contains all installed devices of the Net class */
2852 hDeviceInfo = SetupDiGetClassDevs (&netGuid, NULL, NULL, DIGCF_PRESENT);
2853
2854 if (hDeviceInfo == INVALID_HANDLE_VALUE)
2855 SetErrBreak (("SetupDiGetClassDevs failed (0x%08X)", GetLastError()));
2856
2857 /* enumerate the driver info list */
2858 while (TRUE)
2859 {
2860 TCHAR *deviceHwid;
2861
2862 ok = SetupDiEnumDeviceInfo (hDeviceInfo, index, &DeviceInfoData);
2863
2864 if (!ok)
2865 {
2866 if (GetLastError() == ERROR_NO_MORE_ITEMS)
2867 break;
2868 else
2869 {
2870 index++;
2871 continue;
2872 }
2873 }
2874
2875 /* try to get the hardware ID registry property */
2876 ok = SetupDiGetDeviceRegistryProperty (hDeviceInfo,
2877 &DeviceInfoData,
2878 SPDRP_HARDWAREID,
2879 NULL,
2880 NULL,
2881 0,
2882 &size);
2883 if (!ok)
2884 {
2885 if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
2886 {
2887 index++;
2888 continue;
2889 }
2890
2891 deviceHwid = (TCHAR *) malloc (size);
2892 ok = SetupDiGetDeviceRegistryProperty (hDeviceInfo,
2893 &DeviceInfoData,
2894 SPDRP_HARDWAREID,
2895 NULL,
2896 (PBYTE)deviceHwid,
2897 size,
2898 NULL);
2899 if (!ok)
2900 {
2901 free (deviceHwid);
2902 deviceHwid = NULL;
2903 index++;
2904 continue;
2905 }
2906 }
2907 else
2908 {
2909 /* something is wrong. This shouldn't have worked with a NULL buffer */
2910 index++;
2911 continue;
2912 }
2913
2914 for (TCHAR *t = deviceHwid;
2915 t && *t && t < &deviceHwid[size / sizeof(TCHAR)];
2916 t += _tcslen (t) + 1)
2917 {
2918 if (!_tcsicmp (DRIVERHWID, t))
2919 {
2920 /* get the device instance ID */
2921 TCHAR devID [MAX_DEVICE_ID_LEN];
2922 if (CM_Get_Device_ID(DeviceInfoData.DevInst,
2923 devID, MAX_DEVICE_ID_LEN, 0) == CR_SUCCESS)
2924 {
2925 /* compare to what we determined before */
2926 if (wcscmp(devID, lszPnPInstanceId) == 0)
2927 {
2928 found = TRUE;
2929 break;
2930 }
2931 }
2932 }
2933 }
2934
2935 if (deviceHwid)
2936 {
2937 free (deviceHwid);
2938 deviceHwid = NULL;
2939 }
2940
2941 if (found)
2942 break;
2943
2944 index++;
2945 }
2946
2947 if (found == FALSE)
2948 SetErrBreak ((tr ("Host Interface Network driver not found (0x%08X)"),
2949 GetLastError()));
2950
2951 ok = SetupDiSetSelectedDevice (hDeviceInfo, &DeviceInfoData);
2952 if (!ok)
2953 SetErrBreak (("SetupDiSetSelectedDevice failed (0x%08X)",
2954 GetLastError()));
2955
2956 ok = SetupDiCallClassInstaller (DIF_REMOVE, hDeviceInfo, &DeviceInfoData);
2957 if (!ok)
2958 SetErrBreak (("SetupDiCallClassInstaller (DIF_REMOVE) failed (0x%08X)",
2959 GetLastError()));
2960 }
2961 while (0);
2962
2963 /* clean up the device info set */
2964 if (hDeviceInfo != INVALID_HANDLE_VALUE)
2965 SetupDiDestroyDeviceInfoList (hDeviceInfo);
2966
2967 if (VBOX_FAILURE (vrc))
2968 break;
2969 }
2970 while (0);
2971
2972 LogFlowFunc (("vrc=%Vrc\n", vrc));
2973 LogFlowFuncLeave();
2974 return vrc;
2975}
2976
2977#undef SetErrBreak
2978
2979/* static */
2980HRESULT Host::networkInterfaceHelperClient (SVCHlpClient *aClient,
2981 Progress *aProgress,
2982 void *aUser, int *aVrc)
2983{
2984 LogFlowFuncEnter();
2985 LogFlowFunc (("aClient={%p}, aProgress={%p}, aUser={%p}\n",
2986 aClient, aProgress, aUser));
2987
2988 AssertReturn ((aClient == NULL && aProgress == NULL && aVrc == NULL) ||
2989 (aClient != NULL && aProgress != NULL && aVrc != NULL),
2990 E_POINTER);
2991 AssertReturn (aUser, E_POINTER);
2992
2993 std::auto_ptr <NetworkInterfaceHelperClientData>
2994 d (static_cast <NetworkInterfaceHelperClientData *> (aUser));
2995
2996 if (aClient == NULL)
2997 {
2998 /* "cleanup only" mode, just return (it will free aUser) */
2999 return S_OK;
3000 }
3001
3002 HRESULT rc = S_OK;
3003 int vrc = VINF_SUCCESS;
3004
3005 switch (d->msgCode)
3006 {
3007 case SVCHlpMsg::CreateHostNetworkInterface:
3008 {
3009 LogFlowFunc (("CreateHostNetworkInterface:\n"));
3010 LogFlowFunc (("Network connection name = '%ls'\n", d->name.raw()));
3011
3012 /* write message and parameters */
3013 vrc = aClient->write (d->msgCode);
3014 if (VBOX_FAILURE (vrc)) break;
3015 vrc = aClient->write (Utf8Str (d->name));
3016 if (VBOX_FAILURE (vrc)) break;
3017
3018 /* wait for a reply */
3019 bool endLoop = false;
3020 while (!endLoop)
3021 {
3022 SVCHlpMsg::Code reply = SVCHlpMsg::Null;
3023
3024 vrc = aClient->read (reply);
3025 if (VBOX_FAILURE (vrc)) break;
3026
3027 switch (reply)
3028 {
3029 case SVCHlpMsg::CreateHostNetworkInterface_OK:
3030 {
3031 /* read the GUID */
3032 Guid guid;
3033 vrc = aClient->read (guid);
3034 if (VBOX_FAILURE (vrc)) break;
3035
3036 LogFlowFunc (("Network connection GUID = {%Vuuid}\n", guid.raw()));
3037
3038 /* initialize the object returned to the caller by
3039 * CreateHostNetworkInterface() */
3040 rc = d->iface->init (d->name, guid);
3041 endLoop = true;
3042 break;
3043 }
3044 case SVCHlpMsg::Error:
3045 {
3046 /* read the error message */
3047 Utf8Str errMsg;
3048 vrc = aClient->read (errMsg);
3049 if (VBOX_FAILURE (vrc)) break;
3050
3051 rc = setError (E_FAIL, errMsg);
3052 endLoop = true;
3053 break;
3054 }
3055 default:
3056 {
3057 endLoop = true;
3058 ComAssertMsgFailedBreak ((
3059 "Invalid message code %d (%08lX)\n",
3060 reply, reply),
3061 rc = E_FAIL);
3062 }
3063 }
3064 }
3065
3066 break;
3067 }
3068 case SVCHlpMsg::RemoveHostNetworkInterface:
3069 {
3070 LogFlowFunc (("RemoveHostNetworkInterface:\n"));
3071 LogFlowFunc (("Network connection GUID = {%Vuuid}\n", d->guid.raw()));
3072
3073 /* write message and parameters */
3074 vrc = aClient->write (d->msgCode);
3075 if (VBOX_FAILURE (vrc)) break;
3076 vrc = aClient->write (d->guid);
3077 if (VBOX_FAILURE (vrc)) break;
3078
3079 /* wait for a reply */
3080 bool endLoop = false;
3081 while (!endLoop)
3082 {
3083 SVCHlpMsg::Code reply = SVCHlpMsg::Null;
3084
3085 vrc = aClient->read (reply);
3086 if (VBOX_FAILURE (vrc)) break;
3087
3088 switch (reply)
3089 {
3090 case SVCHlpMsg::OK:
3091 {
3092 /* no parameters */
3093 rc = S_OK;
3094 endLoop = true;
3095 break;
3096 }
3097 case SVCHlpMsg::Error:
3098 {
3099 /* read the error message */
3100 Utf8Str errMsg;
3101 vrc = aClient->read (errMsg);
3102 if (VBOX_FAILURE (vrc)) break;
3103
3104 rc = setError (E_FAIL, errMsg);
3105 endLoop = true;
3106 break;
3107 }
3108 default:
3109 {
3110 endLoop = true;
3111 ComAssertMsgFailedBreak ((
3112 "Invalid message code %d (%08lX)\n",
3113 reply, reply),
3114 rc = E_FAIL);
3115 }
3116 }
3117 }
3118
3119 break;
3120 }
3121 default:
3122 ComAssertMsgFailedBreak ((
3123 "Invalid message code %d (%08lX)\n",
3124 d->msgCode, d->msgCode),
3125 rc = E_FAIL);
3126 }
3127
3128 if (aVrc)
3129 *aVrc = vrc;
3130
3131 LogFlowFunc (("rc=0x%08X, vrc=%Vrc\n", rc, vrc));
3132 LogFlowFuncLeave();
3133 return rc;
3134}
3135
3136/* static */
3137int Host::networkInterfaceHelperServer (SVCHlpClient *aClient,
3138 SVCHlpMsg::Code aMsgCode)
3139{
3140 LogFlowFuncEnter();
3141 LogFlowFunc (("aClient={%p}, aMsgCode=%d\n", aClient, aMsgCode));
3142
3143 AssertReturn (aClient, VERR_INVALID_POINTER);
3144
3145 int vrc = VINF_SUCCESS;
3146
3147 switch (aMsgCode)
3148 {
3149 case SVCHlpMsg::CreateHostNetworkInterface:
3150 {
3151 LogFlowFunc (("CreateHostNetworkInterface:\n"));
3152
3153 Utf8Str name;
3154 vrc = aClient->read (name);
3155 if (VBOX_FAILURE (vrc)) break;
3156
3157 Guid guid;
3158 Utf8Str errMsg;
3159 vrc = createNetworkInterface (aClient, name, guid, errMsg);
3160
3161 if (VBOX_SUCCESS (vrc))
3162 {
3163 /* write success followed by GUID */
3164 vrc = aClient->write (SVCHlpMsg::CreateHostNetworkInterface_OK);
3165 if (VBOX_FAILURE (vrc)) break;
3166 vrc = aClient->write (guid);
3167 if (VBOX_FAILURE (vrc)) break;
3168 }
3169 else
3170 {
3171 /* write failure followed by error message */
3172 if (errMsg.isEmpty())
3173 errMsg = Utf8StrFmt ("Unspecified error (%Vrc)", vrc);
3174 vrc = aClient->write (SVCHlpMsg::Error);
3175 if (VBOX_FAILURE (vrc)) break;
3176 vrc = aClient->write (errMsg);
3177 if (VBOX_FAILURE (vrc)) break;
3178 }
3179
3180 break;
3181 }
3182 case SVCHlpMsg::RemoveHostNetworkInterface:
3183 {
3184 LogFlowFunc (("RemoveHostNetworkInterface:\n"));
3185
3186 Guid guid;
3187 vrc = aClient->read (guid);
3188 if (VBOX_FAILURE (vrc)) break;
3189
3190 Utf8Str errMsg;
3191 vrc = removeNetworkInterface (aClient, guid, errMsg);
3192
3193 if (VBOX_SUCCESS (vrc))
3194 {
3195 /* write parameter-less success */
3196 vrc = aClient->write (SVCHlpMsg::OK);
3197 if (VBOX_FAILURE (vrc)) break;
3198 }
3199 else
3200 {
3201 /* write failure followed by error message */
3202 if (errMsg.isEmpty())
3203 errMsg = Utf8StrFmt ("Unspecified error (%Vrc)", vrc);
3204 vrc = aClient->write (SVCHlpMsg::Error);
3205 if (VBOX_FAILURE (vrc)) break;
3206 vrc = aClient->write (errMsg);
3207 if (VBOX_FAILURE (vrc)) break;
3208 }
3209
3210 break;
3211 }
3212 default:
3213 AssertMsgFailedBreak ((
3214 "Invalid message code %d (%08lX)\n", aMsgCode, aMsgCode),
3215 VERR_GENERAL_FAILURE);
3216 }
3217
3218 LogFlowFunc (("vrc=%Vrc\n", vrc));
3219 LogFlowFuncLeave();
3220 return vrc;
3221}
3222
3223#endif /* RT_OS_WINDOWS */
3224
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