VirtualBox

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

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

Main: Improved Machine incapsulation by removing data(), userData(), hwData(), hdData() and other similar methods which also fixed the VBoxSVC crash when changing a setting of a valid VM through VBoxManage if there were one or more inaccessible VMs (#2466).

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