VirtualBox

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

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

Prototyping USB support for OS/2 hosts.

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