VirtualBox

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

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

filteheader.

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette