VirtualBox

source: vbox/trunk/src/VBox/Main/linux/HostHardwareLinux.cpp@ 18622

Last change on this file since 18622 was 17929, checked in by vboxsync, 16 years ago

Main: enabled -Wunused; fixed some warnings

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 55.2 KB
Line 
1/* $Id: HostHardwareLinux.cpp 17929 2009-03-16 13:09:36Z vboxsync $ */
2/** @file
3 * Classes for handling hardware detection under Linux. Please feel free to
4 * expand these to work for other systems (Solaris!) or to add new ones for
5 * other systems.
6 */
7
8/*
9 * Copyright (C) 2008 Sun Microsystems, Inc.
10 *
11 * This file is part of VirtualBox Open Source Edition (OSE), as
12 * available from http://www.virtualbox.org. This file is free software;
13 * you can redistribute it and/or modify it under the terms of the GNU
14 * General Public License (GPL) as published by the Free Software
15 * Foundation, in version 2 as it comes in the "COPYING" file of the
16 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
17 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
18 *
19 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
20 * Clara, CA 95054 USA or visit http://www.sun.com if you need
21 * additional information or have any questions.
22 */
23
24#define LOG_GROUP LOG_GROUP_MAIN
25
26/*******************************************************************************
27* Header Files *
28*******************************************************************************/
29
30#include <HostHardwareLinux.h>
31
32#include <VBox/log.h>
33
34#include <iprt/env.h>
35#include <iprt/mem.h>
36#include <iprt/string.h>
37
38#ifdef RT_OS_LINUX
39# include <sys/types.h>
40# include <sys/stat.h>
41# include <unistd.h>
42# include <sys/ioctl.h>
43# include <fcntl.h>
44# include <mntent.h>
45/* bird: This is a hack to work around conflicts between these linux kernel headers
46 * and the GLIBC tcpip headers. They have different declarations of the 4
47 * standard byte order functions. */
48// # define _LINUX_BYTEORDER_GENERIC_H
49# define _LINUX_BYTEORDER_SWABB_H
50# include <linux/cdrom.h>
51# ifdef VBOX_WITH_DBUS
52# include <vbox-dbus.h>
53# endif
54# include <errno.h>
55#endif /* RT_OS_LINUX */
56#include <string>
57#include <vector>
58
59/*******************************************************************************
60* Global Variables *
61*******************************************************************************/
62
63bool g_testHostHardwareLinux = false;
64static bool testing () { return g_testHostHardwareLinux; }
65
66/*******************************************************************************
67* Typedefs and Defines *
68*******************************************************************************/
69
70/** When waiting for hotplug events, we currently restart the wait after at
71 * most this many milliseconds. */
72enum { DBUS_POLL_TIMEOUT = 2000 /* ms */ };
73
74
75static bool validateDevice(const char *deviceNode, bool isDVD);
76static int getDriveInfoFromEnv(const char *pszVar, DriveInfoList *pList,
77 bool isDVD, bool *pfSuccess);
78static int getDVDInfoFromMTab(char *mountTable, DriveInfoList *pList);
79#ifdef VBOX_WITH_DBUS
80/* These must be extern to be usable in the RTMemAutoPtr template */
81extern void VBoxHalShutdown (DBusConnection *pConnection);
82extern void VBoxHalShutdownPrivate (DBusConnection *pConnection);
83extern void VBoxDBusConnectionUnref(DBusConnection *pConnection);
84extern void VBoxDBusConnectionCloseAndUnref(DBusConnection *pConnection);
85extern void VBoxDBusMessageUnref(DBusMessage *pMessage);
86
87static int halInit(RTMemAutoPtr <DBusConnection, VBoxHalShutdown> *pConnection);
88static int halInitPrivate(RTMemAutoPtr <DBusConnection, VBoxHalShutdownPrivate> *pConnection);
89static int halFindDeviceStringMatch (DBusConnection *pConnection,
90 const char *pszKey, const char *pszValue,
91 RTMemAutoPtr <DBusMessage, VBoxDBusMessageUnref> *pMessage);
92/*
93static int halFindDeviceStringMatchVector (DBusConnection *pConnection,
94 const char *pszKey,
95 const char *pszValue,
96 std::vector<std::string> *pMatches);
97*/
98static int halGetPropertyStrings (DBusConnection *pConnection,
99 const char *pszUdi, size_t cKeys,
100 const char **papszKeys, char **papszValues,
101 RTMemAutoPtr <DBusMessage, VBoxDBusMessageUnref> *pMessage);
102/*
103static int halGetPropertyStringsVector (DBusConnection *pConnection,
104 const char *pszUdi, size_t cProps,
105 const char **papszKeys,
106 std::vector<std::string> *pMatches,
107 bool *pfMatches, bool *pfSuccess);
108*/
109static int getDriveInfoFromHal(DriveInfoList *pList, bool isDVD,
110 bool *pfSuccess);
111static int getUSBDeviceInfoFromHal(USBDeviceInfoList *pList, bool *pfSuccess);
112static int getOldUSBDeviceInfoFromHal(USBDeviceInfoList *pList, bool *pfSuccess);
113static int getUSBInterfacesFromHal(std::vector <std::string> *pList,
114 const char *pcszUdi, bool *pfSuccess);
115static DBusHandlerResult dbusFilterFunction (DBusConnection *pConnection,
116 DBusMessage *pMessage, void *pvUser);
117#endif /* VBOX_WITH_DBUS */
118
119int VBoxMainDriveInfo::updateDVDs ()
120{
121 LogFlowThisFunc (("entered\n"));
122 int rc = VINF_SUCCESS;
123 bool success = false; /* Have we succeeded in finding anything yet? */
124 try
125 {
126 mDVDList.clear ();
127#if defined(RT_OS_LINUX)
128#ifdef VBOX_WITH_DBUS
129 if (RT_SUCCESS (rc) && RT_SUCCESS(VBoxLoadDBusLib()) && (!success || testing()))
130 rc = getDriveInfoFromHal(&mDVDList, true /* isDVD */, &success);
131#endif /* VBOX_WITH_DBUS defined */
132 // On Linux without hal, the situation is much more complex. We will take a
133 // heuristical approach and also allow the user to specify a list of host
134 // CDROMs using an environment variable.
135 // The general strategy is to try some known device names and see of they
136 // exist. At last, we'll enumerate the /etc/fstab file (luckily there's an
137 // API to parse it) for CDROM devices. Ok, let's start!
138 if (RT_SUCCESS (rc) && (!success || testing()))
139 rc = getDriveInfoFromEnv ("VBOX_CDROM", &mDVDList, true /* isDVD */,
140 &success);
141 if (RT_SUCCESS (rc) && (!success || testing()))
142 {
143 // this is a good guess usually
144 if (validateDevice("/dev/cdrom", true))
145 try
146 {
147 mDVDList.push_back (DriveInfo ("/dev/cdrom"));
148 }
149 catch (std::bad_alloc)
150 {
151 rc = VERR_NO_MEMORY;
152 }
153
154 // check the mounted drives
155 rc = getDVDInfoFromMTab((char*)"/etc/mtab", &mDVDList);
156
157 // check the drives that can be mounted
158 if (RT_SUCCESS (rc))
159 rc = getDVDInfoFromMTab((char*)"/etc/fstab", &mDVDList);
160 }
161#endif
162 }
163 catch (std::bad_alloc)
164 {
165 rc = VERR_NO_MEMORY;
166 }
167 LogFlowThisFunc (("rc=%Rrc\n", rc));
168 return rc;
169}
170
171int VBoxMainDriveInfo::updateFloppies ()
172{
173 LogFlowThisFunc (("entered\n"));
174 int rc = VINF_SUCCESS;
175 bool success = false; /* Have we succeeded in finding anything yet? */
176 try
177 {
178 mFloppyList.clear ();
179#if defined(RT_OS_LINUX)
180#ifdef VBOX_WITH_DBUS
181 if ( RT_SUCCESS (rc)
182 && RT_SUCCESS(VBoxLoadDBusLib())
183 && (!success || testing()))
184 rc = getDriveInfoFromHal(&mFloppyList, false /* isDVD */, &success);
185#endif /* VBOX_WITH_DBUS defined */
186 // As with the CDROMs, on Linux we have to take a multi-level approach
187 // involving parsing the mount tables. As this is not bulletproof, we'll
188 // give the user the chance to override the detection by an environment
189 // variable and skip the detection.
190 if (RT_SUCCESS (rc) && (!success || testing()))
191 rc = getDriveInfoFromEnv ("VBOX_FLOPPY", &mFloppyList, false /* isDVD */,
192 &success);
193
194 if (RT_SUCCESS (rc) && (!success || testing()))
195 {
196 // we assume that a floppy is always /dev/fd[x] with x from 0 to 7
197 char devName[10];
198 for (int i = 0; i <= 7; i++)
199 {
200 sprintf(devName, "/dev/fd%d", i);
201 if (validateDevice(devName, false))
202 try
203 {
204 mFloppyList.push_back (DriveInfo (devName));
205 }
206 catch (std::bad_alloc)
207 {
208 rc = VERR_NO_MEMORY;
209 }
210 }
211 }
212#endif
213 }
214 catch (std::bad_alloc)
215 {
216 rc = VERR_NO_MEMORY;
217 }
218 LogFlowThisFunc (("rc=%Rrc\n", rc));
219 return rc;
220}
221
222int VBoxMainUSBDeviceInfo::UpdateDevices ()
223{
224 LogFlowThisFunc (("entered\n"));
225 int rc = VINF_SUCCESS;
226 bool success = false; /* Have we succeeded in finding anything yet? */
227 try
228 {
229 bool halSuccess = false;
230 mDeviceList.clear();
231#if defined(RT_OS_LINUX)
232#ifdef VBOX_WITH_DBUS
233 if ( RT_SUCCESS (rc)
234 && RT_SUCCESS(VBoxLoadDBusLib())
235 && (!success || testing()))
236 rc = getUSBDeviceInfoFromHal(&mDeviceList, &halSuccess);
237 /* Try the old API if the new one *succeeded* as only one of them will
238 * pick up devices anyway. */
239 if (RT_SUCCESS (rc) && halSuccess && (!success || testing()))
240 rc = getOldUSBDeviceInfoFromHal(&mDeviceList, &halSuccess);
241 if (!success)
242 success = halSuccess;
243#endif /* VBOX_WITH_DBUS defined */
244#endif /* RT_OS_LINUX */
245 }
246 catch (std::bad_alloc)
247 {
248 rc = VERR_NO_MEMORY;
249 }
250 LogFlowThisFunc (("rc=%Rrc\n", rc));
251 return rc;
252}
253
254struct VBoxMainHotplugWaiter::Context
255{
256#if defined RT_OS_LINUX && defined VBOX_WITH_DBUS
257 /** The connection to DBus */
258 RTMemAutoPtr <DBusConnection, VBoxHalShutdownPrivate> mConnection;
259 /** Semaphore which is set when a device is hotplugged and reset when
260 * it is read. */
261 volatile bool mTriggered;
262 /** A flag to say that we wish to interrupt the current wait. */
263 volatile bool mInterrupt;
264 /** Constructor */
265 Context() : mTriggered(false), mInterrupt(false) {}
266#endif /* defined RT_OS_LINUX && defined VBOX_WITH_DBUS */
267};
268
269/* This constructor sets up a private connection to the DBus daemon, connects
270 * to the hal service and installs a filter which sets the mTriggered flag in
271 * the Context structure when a device (not necessarily USB) is added or
272 * removed. */
273VBoxMainHotplugWaiter::VBoxMainHotplugWaiter ()
274{
275#if defined RT_OS_LINUX && defined VBOX_WITH_DBUS
276 int rc = VINF_SUCCESS;
277
278 mContext = new Context;
279 if (RT_SUCCESS(VBoxLoadDBusLib()))
280 {
281 for (unsigned i = 0; RT_SUCCESS(rc) && i < 5 && !mContext->mConnection; ++i)
282 {
283 rc = halInitPrivate (&mContext->mConnection);
284 }
285 if (!mContext->mConnection)
286 rc = VERR_NOT_SUPPORTED;
287 DBusMessage *pMessage;
288 while ( RT_SUCCESS (rc)
289 && (pMessage = dbus_connection_pop_message (mContext->mConnection.get())) != NULL)
290 dbus_message_unref (pMessage); /* empty the message queue. */
291 if ( RT_SUCCESS (rc)
292 && !dbus_connection_add_filter (mContext->mConnection.get(),
293 dbusFilterFunction,
294 (void *) &mContext->mTriggered, NULL))
295 rc = VERR_NO_MEMORY;
296 if (RT_FAILURE (rc))
297 mContext->mConnection.reset();
298 }
299#endif /* defined RT_OS_LINUX && defined VBOX_WITH_DBUS */
300}
301
302/* Destructor */
303VBoxMainHotplugWaiter::~VBoxMainHotplugWaiter ()
304{
305#if defined RT_OS_LINUX && defined VBOX_WITH_DBUS
306 if (!!mContext->mConnection)
307 dbus_connection_remove_filter (mContext->mConnection.get(), dbusFilterFunction,
308 (void *) &mContext->mTriggered);
309 delete mContext;
310#endif /* defined RT_OS_LINUX && defined VBOX_WITH_DBUS */
311}
312
313/* Currently this is implemented using a timed out wait on our private DBus
314 * connection. Because the connection is private we don't have to worry about
315 * blocking other users. */
316int VBoxMainHotplugWaiter::Wait(unsigned cMillies)
317{
318 int rc = VINF_SUCCESS;
319#if defined RT_OS_LINUX && defined VBOX_WITH_DBUS
320 if (!mContext->mConnection)
321 rc = VERR_NOT_SUPPORTED;
322 bool connected = true;
323 mContext->mTriggered = false;
324 mContext->mInterrupt = false;
325 unsigned cRealMillies;
326 if (cMillies != RT_INDEFINITE_WAIT)
327 cRealMillies = cMillies;
328 else
329 cRealMillies = DBUS_POLL_TIMEOUT;
330 while ( RT_SUCCESS (rc) && connected && !mContext->mTriggered
331 && !mContext->mInterrupt)
332 {
333 connected = dbus_connection_read_write_dispatch (mContext->mConnection.get(),
334 cRealMillies);
335 if (mContext->mInterrupt)
336 LogFlowFunc(("wait loop interrupted\n"));
337 if (cMillies != RT_INDEFINITE_WAIT)
338 mContext->mInterrupt = true;
339 }
340 if (!connected)
341 rc = VERR_TRY_AGAIN;
342#else /* !(defined RT_OS_LINUX && defined VBOX_WITH_DBUS) */
343 rc = VERR_NOT_IMPLEMENTED;
344#endif /* !(defined RT_OS_LINUX && defined VBOX_WITH_DBUS) */
345 return rc;
346}
347
348/* Set a flag to tell the Wait not to resume next time it times out. */
349void VBoxMainHotplugWaiter::Interrupt()
350{
351#if defined RT_OS_LINUX && defined VBOX_WITH_DBUS
352 LogFlowFunc(("\n"));
353 mContext->mInterrupt = true;
354#endif /* defined RT_OS_LINUX && defined VBOX_WITH_DBUS */
355}
356
357#ifdef RT_OS_LINUX
358/**
359 * Helper function to check whether the given device node is a valid drive
360 */
361/* static */
362bool validateDevice(const char *deviceNode, bool isDVD)
363{
364 AssertReturn(VALID_PTR (deviceNode), VERR_INVALID_POINTER);
365 LogFlowFunc (("deviceNode=%s, isDVD=%d\n", deviceNode, isDVD));
366 struct stat statInfo;
367 bool retValue = false;
368
369 // sanity check
370 if (!deviceNode)
371 {
372 return false;
373 }
374
375 // first a simple stat() call
376 if (stat(deviceNode, &statInfo) < 0)
377 {
378 return false;
379 } else
380 {
381 if (isDVD)
382 {
383 if (S_ISCHR(statInfo.st_mode) || S_ISBLK(statInfo.st_mode))
384 {
385 int fileHandle;
386 // now try to open the device
387 fileHandle = open(deviceNode, O_RDONLY | O_NONBLOCK, 0);
388 if (fileHandle >= 0)
389 {
390 cdrom_subchnl cdChannelInfo;
391 cdChannelInfo.cdsc_format = CDROM_MSF;
392 // this call will finally reveal the whole truth
393#ifdef RT_OS_LINUX
394 if ((ioctl(fileHandle, CDROMSUBCHNL, &cdChannelInfo) == 0) ||
395 (errno == EIO) || (errno == ENOENT) ||
396 (errno == EINVAL) || (errno == ENOMEDIUM))
397#endif
398 {
399 retValue = true;
400 }
401 close(fileHandle);
402 }
403 }
404 } else
405 {
406 // floppy case
407 if (S_ISCHR(statInfo.st_mode) || S_ISBLK(statInfo.st_mode))
408 {
409 /// @todo do some more testing, maybe a nice IOCTL!
410 retValue = true;
411 }
412 }
413 }
414 LogFlowFunc (("retValue=%d\n", retValue));
415 return retValue;
416}
417#else /* !RT_OS_LINUX */
418# error Port me! Copying code over from HostImpl.cpp should be most of the job though.
419#endif /* !RT_OS_LINUX */
420
421/**
422 * Extract the names of drives from an environment variable and add them to a
423 * list if they are valid.
424 * @returns iprt status code
425 * @param pszVar the name of the environment variable. The variable
426 * value should be a list of device node names, separated
427 * by ':' characters.
428 * @param pList the list to append the drives found to
429 * @param isDVD are we looking for DVD drives or for floppies?
430 * @param pfSuccess this will be set to true if we found at least one drive
431 * and to false otherwise. Optional.
432 */
433/* static */
434int getDriveInfoFromEnv(const char *pszVar, DriveInfoList *pList,
435 bool isDVD, bool *pfSuccess)
436{
437 AssertReturn( VALID_PTR (pszVar) && VALID_PTR (pList)
438 && (pfSuccess == NULL || VALID_PTR (pfSuccess)),
439 VERR_INVALID_POINTER);
440 LogFlowFunc (("pszVar=%s, pList=%p, isDVD=%d, pfSuccess=%p\n", pszVar,
441 pList, isDVD, pfSuccess));
442 int rc = VINF_SUCCESS;
443 bool success = false;
444 RTMemAutoPtr<char, RTStrFree> drive;
445 const char *pszValue = RTEnvGet (pszVar);
446 if (pszValue != NULL)
447 {
448 drive = RTStrDup (pszValue);
449 if (!drive)
450 rc = VERR_NO_MEMORY;
451 }
452 if (pszValue != NULL && RT_SUCCESS (rc))
453 {
454 char *pDrive = drive.get();
455 char *pDriveNext = strchr (pDrive, ':');
456 while (pDrive != NULL && *pDrive != '\0')
457 {
458 if (pDriveNext != NULL)
459 *pDriveNext = '\0';
460 if (validateDevice(pDrive, isDVD))
461 {
462 try
463 {
464 pList->push_back (DriveInfo (pDrive));
465 }
466 catch (std::bad_alloc)
467 {
468 rc = VERR_NO_MEMORY;
469 }
470 success = true;
471 }
472 if (pDriveNext != NULL)
473 {
474 pDrive = pDriveNext + 1;
475 pDriveNext = strchr (pDrive, ':');
476 }
477 else
478 pDrive = NULL;
479 }
480 }
481 if (pfSuccess != NULL)
482 *pfSuccess = success;
483 LogFlowFunc (("rc=%Rrc, success=%d\n", rc, success));
484 return rc;
485}
486
487#ifdef RT_OS_LINUX
488/**
489 * Helper function to parse the given mount file and add found entries
490 */
491/* static */
492int getDVDInfoFromMTab(char *mountTable, DriveInfoList *pList)
493{
494 AssertReturn(VALID_PTR (mountTable) && VALID_PTR (pList),
495 VERR_INVALID_POINTER);
496#ifdef RT_OS_LINUX
497 LogFlowFunc (("mountTable=%s, pList=%p\n", mountTable, pList));
498 int rc = VINF_SUCCESS;
499 FILE *mtab = setmntent(mountTable, "r");
500 if (mtab)
501 {
502 struct mntent *mntent;
503 RTMemAutoPtr <char, RTStrFree> mnt_type, mnt_dev;
504 char *tmp;
505 while (RT_SUCCESS (rc) && (mntent = getmntent(mtab)))
506 {
507 mnt_type = RTStrDup (mntent->mnt_type);
508 mnt_dev = RTStrDup (mntent->mnt_fsname);
509 if (!mnt_type || !mnt_dev)
510 rc = VERR_NO_MEMORY;
511 // supermount fs case
512 if (RT_SUCCESS (rc) && strcmp(mnt_type.get(), "supermount") == 0)
513 {
514 tmp = strstr(mntent->mnt_opts, "fs=");
515 if (tmp)
516 {
517 mnt_type = RTStrDup(tmp + strlen("fs="));
518 if (!mnt_type)
519 rc = VERR_NO_MEMORY;
520 else
521 {
522 tmp = strchr(mnt_type.get(), ',');
523 if (tmp)
524 *tmp = '\0';
525 }
526 }
527 tmp = strstr(mntent->mnt_opts, "dev=");
528 if (tmp)
529 {
530 mnt_dev = RTStrDup(tmp + strlen("dev="));
531 if (!mnt_dev)
532 rc = VERR_NO_MEMORY;
533 else
534 {
535 tmp = strchr(mnt_dev.get(), ',');
536 if (tmp)
537 *tmp = '\0';
538 }
539 }
540 }
541 // use strstr here to cover things fs types like "udf,iso9660"
542 if (RT_SUCCESS (rc) && strstr(mnt_type.get(), "iso9660") == 0)
543 {
544 if (validateDevice(mnt_dev.get(), true))
545 {
546 bool insert = true;
547 struct stat srcInfo;
548 if (stat (mnt_dev.get(), &srcInfo) < 0)
549 insert = false;
550 for (DriveInfoList::const_iterator it = pList->begin();
551 insert && it != pList->end(); ++it)
552 {
553 struct stat destInfo;
554 if ( (stat (it->mDevice.c_str(), &destInfo) == 0)
555 && (srcInfo.st_rdev == destInfo.st_rdev))
556 insert = false;
557 }
558 if (insert)
559 try
560 {
561 pList->push_back (DriveInfo (mnt_dev.get()));
562 }
563 catch (std::bad_alloc)
564 {
565 rc = VERR_NO_MEMORY;
566 }
567 }
568 }
569 }
570 endmntent(mtab);
571 }
572 return rc;
573#endif
574}
575
576#endif /* RT_OS_LINUX */
577
578#if defined(RT_OS_LINUX) && defined(VBOX_WITH_DBUS)
579/** Wrapper class around DBusError for automatic cleanup */
580class autoDBusError
581{
582 DBusError mError;
583public:
584 autoDBusError () { dbus_error_init (&mError); }
585 ~autoDBusError ()
586 {
587 if (IsSet())
588 dbus_error_free (&mError);
589 }
590 DBusError &get () { return mError; }
591 bool IsSet ()
592 {
593 Assert ((mError.name == NULL) == (mError.message == NULL));
594 return (mError.name != NULL);
595 }
596 bool HasName (const char *pszName)
597 {
598 Assert ((mError.name == NULL) == (mError.message == NULL));
599 return (RTStrCmp (mError.name, pszName) == 0);
600 }
601 void FlowLog ()
602 {
603 if (IsSet ())
604 LogFlow(("DBus error %s: %s\n", mError.name, mError.message));
605 }
606};
607
608/**
609 * Helper function for setting up a connection to the DBus daemon and
610 * registering with the hal service.
611 *
612 * @note If libdbus is being loaded at runtime then be sure to call
613 * VBoxDBusCheckPresence before calling this.
614 * @returns iprt status code
615 * @param ppConnection where to store the connection handle
616 */
617/* static */
618int halInit (RTMemAutoPtr <DBusConnection, VBoxHalShutdown> *pConnection)
619{
620 AssertReturn(VALID_PTR (pConnection), VERR_INVALID_POINTER);
621 LogFlowFunc (("pConnection=%p\n", pConnection));
622 int rc = VINF_SUCCESS;
623 bool halSuccess = true;
624 autoDBusError dbusError;
625
626 RTMemAutoPtr <DBusConnection, VBoxDBusConnectionUnref> dbusConnection;
627 dbusConnection = dbus_bus_get (DBUS_BUS_SYSTEM, &dbusError.get());
628 if (!dbusConnection)
629 halSuccess = false;
630 if (halSuccess)
631 {
632 dbus_connection_set_exit_on_disconnect (dbusConnection.get(), false);
633 halSuccess = dbus_bus_name_has_owner (dbusConnection.get(),
634 "org.freedesktop.Hal", &dbusError.get());
635 }
636 if (halSuccess)
637 {
638 dbus_bus_add_match (dbusConnection.get(),
639 "type='signal',"
640 "interface='org.freedesktop.Hal.Manager',"
641 "sender='org.freedesktop.Hal',"
642 "path='/org/freedesktop/Hal/Manager'",
643 &dbusError.get());
644 halSuccess = !dbusError.IsSet();
645 }
646 if (dbusError.HasName (DBUS_ERROR_NO_MEMORY))
647 rc = VERR_NO_MEMORY;
648 if (halSuccess)
649 *pConnection = dbusConnection.release();
650 LogFlowFunc(("rc=%Rrc, (*pConnection).get()=%p\n", rc, (*pConnection).get()));
651 dbusError.FlowLog();
652 return rc;
653}
654
655/**
656 * Helper function for setting up a private connection to the DBus daemon and
657 * registering with the hal service. Private connections are considered
658 * unsociable and should not be used unnecessarily (as per the DBus API docs).
659 *
660 * @note If libdbus is being loaded at runtime then be sure to call
661 * VBoxDBusCheckPresence before calling this.
662 * @returns iprt status code
663 * @param pConnection where to store the connection handle
664 */
665/* static */
666int halInitPrivate (RTMemAutoPtr <DBusConnection, VBoxHalShutdownPrivate> *pConnection)
667{
668 AssertReturn(VALID_PTR (pConnection), VERR_INVALID_POINTER);
669 LogFlowFunc (("pConnection=%p\n", pConnection));
670 int rc = VINF_SUCCESS;
671 bool halSuccess = true;
672 autoDBusError dbusError;
673
674 RTMemAutoPtr <DBusConnection, VBoxDBusConnectionCloseAndUnref> dbusConnection;
675 dbusConnection = dbus_bus_get_private (DBUS_BUS_SYSTEM, &dbusError.get());
676 if (!dbusConnection)
677 halSuccess = false;
678 if (halSuccess)
679 {
680 dbus_connection_set_exit_on_disconnect (dbusConnection.get(), false);
681 halSuccess = dbus_bus_name_has_owner (dbusConnection.get(),
682 "org.freedesktop.Hal", &dbusError.get());
683 }
684 if (halSuccess)
685 {
686 dbus_bus_add_match (dbusConnection.get(),
687 "type='signal',"
688 "interface='org.freedesktop.Hal.Manager',"
689 "sender='org.freedesktop.Hal',"
690 "path='/org/freedesktop/Hal/Manager'",
691 &dbusError.get());
692 halSuccess = !dbusError.IsSet();
693 }
694 if (dbusError.HasName (DBUS_ERROR_NO_MEMORY))
695 rc = VERR_NO_MEMORY;
696 if (halSuccess)
697 *pConnection = dbusConnection.release();
698 LogFlowFunc(("rc=%Rrc, (*pConnection).get()=%p\n", rc, (*pConnection).get()));
699 dbusError.FlowLog();
700 return rc;
701}
702
703/**
704 * Helper function for shutting down a connection to DBus and hal.
705 * @param pConnection the connection handle
706 */
707/* extern */
708void VBoxHalShutdown (DBusConnection *pConnection)
709{
710 AssertReturnVoid(VALID_PTR (pConnection));
711 LogFlowFunc (("pConnection=%p\n", pConnection));
712 autoDBusError dbusError;
713
714 dbus_bus_remove_match (pConnection,
715 "type='signal',"
716 "interface='org.freedesktop.Hal.Manager',"
717 "sender='org.freedesktop.Hal',"
718 "path='/org/freedesktop/Hal/Manager'",
719 &dbusError.get());
720 dbus_connection_unref (pConnection);
721 LogFlowFunc(("returning\n"));
722 dbusError.FlowLog();
723}
724
725/**
726 * Helper function for shutting down a private connection to DBus and hal.
727 * @param pConnection the connection handle
728 */
729/* extern */
730void VBoxHalShutdownPrivate (DBusConnection *pConnection)
731{
732 AssertReturnVoid(VALID_PTR (pConnection));
733 LogFlowFunc (("pConnection=%p\n", pConnection));
734 autoDBusError dbusError;
735
736 dbus_bus_remove_match (pConnection,
737 "type='signal',"
738 "interface='org.freedesktop.Hal.Manager',"
739 "sender='org.freedesktop.Hal',"
740 "path='/org/freedesktop/Hal/Manager'",
741 &dbusError.get());
742 dbus_connection_close (pConnection);
743 dbus_connection_unref (pConnection);
744 LogFlowFunc(("returning\n"));
745 dbusError.FlowLog();
746}
747
748/** Wrapper around dbus_connection_unref. We need this to use it as a real
749 * function in auto pointers, as a function pointer won't wash here. */
750/* extern */
751void VBoxDBusConnectionUnref(DBusConnection *pConnection)
752{
753 dbus_connection_unref(pConnection);
754}
755
756/**
757 * This function closes and unrefs a private connection to dbus. It should
758 * only be called once no-one else is referencing the connection.
759 */
760/* extern */
761void VBoxDBusConnectionCloseAndUnref(DBusConnection *pConnection)
762{
763 dbus_connection_close(pConnection);
764 dbus_connection_unref(pConnection);
765}
766
767/** Wrapper around dbus_message_unref. We need this to use it as a real
768 * function in auto pointers, as a function pointer won't wash here. */
769/* extern */
770void VBoxDBusMessageUnref(DBusMessage *pMessage)
771{
772 dbus_message_unref(pMessage);
773}
774
775/**
776 * Find the UDIs of hal entries that contain Key=Value property.
777 * @returns iprt status code. If a non-fatal error occurs, we return success
778 * but reset pMessage to NULL.
779 * @param pConnection an initialised connection DBus
780 * @param pszKey the property key
781 * @param pszValue the property value
782 * @param pMessage where to store the return DBus message. This must be
783 * parsed to get at the UDIs. NOT optional.
784 */
785/* static */
786int halFindDeviceStringMatch (DBusConnection *pConnection, const char *pszKey,
787 const char *pszValue,
788 RTMemAutoPtr <DBusMessage, VBoxDBusMessageUnref> *pMessage)
789{
790 AssertReturn( VALID_PTR (pConnection) && VALID_PTR (pszKey)
791 && VALID_PTR (pszValue) && VALID_PTR (pMessage),
792 VERR_INVALID_POINTER);
793 LogFlowFunc (("pConnection=%p, pszKey=%s, pszValue=%s, pMessage=%p\n",
794 pConnection, pszKey, pszValue, pMessage));
795 int rc = VINF_SUCCESS; /* We set this to failure on fatal errors. */
796 bool halSuccess = true; /* We set this to false to abort the operation. */
797 autoDBusError dbusError;
798
799 RTMemAutoPtr <DBusMessage, VBoxDBusMessageUnref> message, reply;
800 if (halSuccess && RT_SUCCESS (rc))
801 {
802 message = dbus_message_new_method_call ("org.freedesktop.Hal",
803 "/org/freedesktop/Hal/Manager",
804 "org.freedesktop.Hal.Manager",
805 "FindDeviceStringMatch");
806 if (!message)
807 rc = VERR_NO_MEMORY;
808 }
809 if (halSuccess && RT_SUCCESS (rc))
810 {
811 DBusMessageIter iterAppend;
812 dbus_message_iter_init_append (message.get(), &iterAppend);
813 dbus_message_iter_append_basic (&iterAppend, DBUS_TYPE_STRING, &pszKey);
814 dbus_message_iter_append_basic (&iterAppend, DBUS_TYPE_STRING, &pszValue);
815 reply = dbus_connection_send_with_reply_and_block (pConnection,
816 message.get(), -1,
817 &dbusError.get());
818 if (!reply)
819 halSuccess = false;
820 }
821 *pMessage = reply.release ();
822 LogFlowFunc (("rc=%Rrc, *pMessage.value()=%p\n", rc, (*pMessage).get()));
823 dbusError.FlowLog();
824 return rc;
825}
826
827/**
828 * Find the UDIs of hal entries that contain Key=Value property and return the
829 * result on the end of a vector of std::string.
830 * @returns iprt status code. If a non-fatal error occurs, we return success
831 * but set *pfSuccess to false.
832 * @param pConnection an initialised connection DBus
833 * @param pszKey the property key
834 * @param pszValue the property value
835 * @param pMatches pointer to an array of std::string to append the
836 * results to. NOT optional.
837 * @param pfSuccess will be set to true if the operation succeeds
838 */
839/* static */
840int halFindDeviceStringMatchVector (DBusConnection *pConnection,
841 const char *pszKey, const char *pszValue,
842 std::vector<std::string> *pMatches,
843 bool *pfSuccess)
844{
845 AssertPtrReturn (pConnection, VERR_INVALID_POINTER);
846 AssertPtrReturn (pszKey, VERR_INVALID_POINTER);
847 AssertPtrReturn (pszValue, VERR_INVALID_POINTER);
848 AssertPtrReturn (pMatches, VERR_INVALID_POINTER);
849 AssertReturn (pfSuccess == NULL || VALID_PTR (pfSuccess), VERR_INVALID_POINTER);
850 LogFlowFunc (("pConnection=%p, pszKey=%s, pszValue=%s, pMatches=%p, pfSuccess=%p\n",
851 pConnection, pszKey, pszValue, pMatches, pfSuccess));
852 int rc = VINF_SUCCESS; /* We set this to failure on fatal errors. */
853 bool halSuccess = true; /* We set this to false to abort the operation. */
854
855 RTMemAutoPtr <DBusMessage, VBoxDBusMessageUnref> message, replyFind;
856 DBusMessageIter iterFind, iterUdis;
857
858 if (halSuccess && RT_SUCCESS (rc))
859 {
860 rc = halFindDeviceStringMatch (pConnection, pszKey, pszValue,
861 &replyFind);
862 if (!replyFind)
863 halSuccess = false;
864 }
865 if (halSuccess && RT_SUCCESS (rc))
866 {
867 dbus_message_iter_init (replyFind.get(), &iterFind);
868 if (dbus_message_iter_get_arg_type (&iterFind) != DBUS_TYPE_ARRAY)
869 halSuccess = false;
870 }
871 if (halSuccess && RT_SUCCESS (rc))
872 dbus_message_iter_recurse (&iterFind, &iterUdis);
873 for (; halSuccess && RT_SUCCESS (rc)
874 && dbus_message_iter_get_arg_type (&iterUdis) == DBUS_TYPE_STRING;
875 dbus_message_iter_next(&iterUdis))
876 {
877 /* Now get all UDIs from the iterator */
878 const char *pszUdi;
879 dbus_message_iter_get_basic (&iterUdis, &pszUdi);
880 try
881 {
882 pMatches->push_back(pszUdi);
883 }
884 catch (std::bad_alloc)
885 {
886 rc = VERR_NO_MEMORY;
887 }
888 }
889 if (pfSuccess != NULL)
890 *pfSuccess = halSuccess;
891 LogFlow (("rc=%Rrc, halSuccess=%d\n", rc, halSuccess));
892 return rc;
893}
894
895/**
896 * Read a set of string properties for a device. If some of the properties are
897 * not of type DBUS_TYPE_STRING or do not exist then a NULL pointer will be
898 * returned for them.
899 * @returns iprt status code. If the operation failed for non-fatal reasons
900 * then we return success and leave pMessage untouched - reset it
901 * before the call to detect this.
902 * @param pConnection an initialised connection DBus
903 * @param pszUdi the Udi of the device
904 * @param cProps the number of property values to look up
905 * @param papszKeys the keys of the properties to be looked up
906 * @param papszValues where to store the values of the properties. The
907 * strings returned will be valid until the message
908 * returned in @a ppMessage is freed. Undefined if
909 * the message is NULL.
910 * @param pMessage where to store the return DBus message. The caller
911 * is responsible for freeing this once they have
912 * finished with the value strings. NOT optional.
913 */
914/* static */
915int halGetPropertyStrings (DBusConnection *pConnection, const char *pszUdi,
916 size_t cProps, const char **papszKeys,
917 char **papszValues,
918 RTMemAutoPtr <DBusMessage, VBoxDBusMessageUnref> *pMessage)
919{
920 AssertReturn( VALID_PTR (pConnection) && VALID_PTR (pszUdi)
921 && VALID_PTR (papszKeys) && VALID_PTR (papszValues)
922 && VALID_PTR (pMessage),
923 VERR_INVALID_POINTER);
924 LogFlowFunc (("pConnection=%p, pszUdi=%s, cProps=%llu, papszKeys=%p, papszValues=%p, pMessage=%p\n",
925 pConnection, pszUdi, cProps, papszKeys, papszValues, pMessage));
926 int rc = VINF_SUCCESS; /* We set this to failure on fatal errors. */
927 bool halSuccess = true; /* We set this to false to abort the operation. */
928 autoDBusError dbusError;
929
930 RTMemAutoPtr <DBusMessage, VBoxDBusMessageUnref> message, reply;
931 DBusMessageIter iterGet, iterProps;
932
933 /* Initialise the return array to NULLs */
934 for (size_t i = 0; i < cProps; ++i)
935 papszValues[i] = NULL;
936
937 /* Send a GetAllProperties message to hald */
938 message = dbus_message_new_method_call ("org.freedesktop.Hal", pszUdi,
939 "org.freedesktop.Hal.Device",
940 "GetAllProperties");
941 if (!message)
942 rc = VERR_NO_MEMORY;
943 if (halSuccess && RT_SUCCESS (rc))
944 {
945 reply = dbus_connection_send_with_reply_and_block (pConnection,
946 message.get(), -1,
947 &dbusError.get());
948 if (!reply)
949 halSuccess = false;
950 }
951
952 /* Parse the reply */
953 if (halSuccess && RT_SUCCESS (rc))
954 {
955 dbus_message_iter_init (reply.get(), &iterGet);
956 if ( dbus_message_iter_get_arg_type (&iterGet) != DBUS_TYPE_ARRAY
957 && dbus_message_iter_get_element_type (&iterGet) != DBUS_TYPE_DICT_ENTRY)
958 halSuccess = false;
959 }
960 if (halSuccess && RT_SUCCESS (rc))
961 dbus_message_iter_recurse (&iterGet, &iterProps);
962 /* Go through all entries in the reply and see if any match our keys. */
963 while ( halSuccess && RT_SUCCESS (rc)
964 && dbus_message_iter_get_arg_type (&iterProps)
965 == DBUS_TYPE_DICT_ENTRY)
966 {
967 const char *pszKey;
968 DBusMessageIter iterEntry, iterValue;
969 dbus_message_iter_recurse (&iterProps, &iterEntry);
970 dbus_message_iter_get_basic (&iterEntry, &pszKey);
971 dbus_message_iter_next (&iterEntry);
972 dbus_message_iter_recurse (&iterEntry, &iterValue);
973 /* Fill in any matches. */
974 for (size_t i = 0; i < cProps; ++i)
975 if (strcmp (pszKey, papszKeys[i]) == 0)
976 {
977 if (dbus_message_iter_get_arg_type (&iterValue) == DBUS_TYPE_STRING)
978 dbus_message_iter_get_basic (&iterValue, &papszValues[i]);
979 }
980 dbus_message_iter_next (&iterProps);
981 }
982 if (RT_SUCCESS (rc) && halSuccess)
983 *pMessage = reply.release();
984 if (dbusError.HasName (DBUS_ERROR_NO_MEMORY))
985 rc = VERR_NO_MEMORY;
986 LogFlowFunc (("rc=%Rrc, *pMessage.value()=%p\n", rc, (*pMessage).get()));
987 dbusError.FlowLog();
988 return rc;
989}
990
991/**
992 * Read a set of string properties for a device. If some properties do not
993 * exist or are not of type DBUS_TYPE_STRING, we will still fetch the others.
994 * @returns iprt status code. If the operation failed for non-fatal reasons
995 * then we return success and set *pfSuccess to false.
996 * @param pConnection an initialised connection DBus
997 * @param pszUdi the Udi of the device
998 * @param cProps the number of property values to look up
999 * @param papszKeys the keys of the properties to be looked up
1000 * @param pMatches pointer to an empty array of std::string to append the
1001 * results to. NOT optional.
1002 * @param pfMatches pointer to an array of boolean values indicating
1003 * whether the respective property is a string. If this
1004 * is not supplied then all properties must be strings
1005 * for the operation to be considered successful
1006 * @param pfSuccess will be set to true if the operation succeeds
1007 */
1008/* static */
1009int halGetPropertyStringsVector (DBusConnection *pConnection,
1010 const char *pszUdi, size_t cProps,
1011 const char **papszKeys,
1012 std::vector<std::string> *pMatches,
1013 bool *pfMatches, bool *pfSuccess)
1014{
1015 AssertPtrReturn (pConnection, VERR_INVALID_POINTER);
1016 AssertPtrReturn (pszUdi, VERR_INVALID_POINTER);
1017 AssertPtrReturn (papszKeys, VERR_INVALID_POINTER);
1018 AssertPtrReturn (pMatches, VERR_INVALID_POINTER);
1019 AssertReturn ((pfMatches == NULL) || VALID_PTR (pfMatches), VERR_INVALID_POINTER);
1020 AssertReturn ((pfSuccess == NULL) || VALID_PTR (pfSuccess), VERR_INVALID_POINTER);
1021 AssertReturn(pMatches->empty(), VERR_INVALID_PARAMETER);
1022 LogFlowFunc (("pConnection=%p, pszUdi=%s, cProps=%llu, papszKeys=%p, pMatches=%p, pfMatches=%p, pfSuccess=%p\n",
1023 pConnection, pszUdi, cProps, papszKeys, pMatches, pfMatches, pfSuccess));
1024 RTMemAutoPtr <char *> values(cProps);
1025 RTMemAutoPtr <DBusMessage, VBoxDBusMessageUnref> message;
1026 bool halSuccess = true;
1027 int rc = halGetPropertyStrings (pConnection, pszUdi, cProps, papszKeys,
1028 values.get(), &message);
1029 if (!message)
1030 halSuccess = false;
1031 for (size_t i = 0; RT_SUCCESS(rc) && halSuccess && i < cProps; ++i)
1032 {
1033 bool fMatches = values[i] != NULL;
1034 if (pfMatches != NULL)
1035 pfMatches[i] = fMatches;
1036 else
1037 halSuccess = fMatches;
1038 try
1039 {
1040 pMatches->push_back(fMatches ? values[i] : "");
1041 }
1042 catch (std::bad_alloc)
1043 {
1044 rc = VERR_NO_MEMORY;
1045 }
1046 }
1047 if (pfSuccess != NULL)
1048 *pfSuccess = halSuccess;
1049 if (RT_SUCCESS(rc) && halSuccess)
1050 {
1051 Assert (pMatches->size() == cProps);
1052 AssertForEach (j, size_t, 0, cProps, (pfMatches == NULL)
1053 || (pfMatches[j] == true)
1054 || ((pfMatches[j] == false) && (pMatches[j].size() == 0)));
1055 }
1056 LogFlowFunc (("rc=%Rrc, halSuccess=%d\n", rc, halSuccess));
1057 return rc;
1058}
1059
1060/**
1061 * Helper function to query the hal subsystem for information about drives
1062 * attached to the system.
1063 * @returns iprt status code
1064 * @param pList where to add information about the drives detected
1065 * @param isDVD are we looking for DVDs or floppies?
1066 * @param pfSuccess will be set to true if all interactions with hal
1067 * succeeded and to false otherwise. Optional.
1068 *
1069 * @returns IPRT status code
1070 */
1071/* static */
1072int getDriveInfoFromHal(DriveInfoList *pList, bool isDVD, bool *pfSuccess)
1073{
1074 AssertReturn(VALID_PTR (pList) && (pfSuccess == NULL || VALID_PTR (pfSuccess)),
1075 VERR_INVALID_POINTER);
1076 LogFlowFunc (("pList=%p, isDVD=%d, pfSuccess=%p\n", pList, isDVD, pfSuccess));
1077 int rc = VINF_SUCCESS; /* We set this to failure on fatal errors. */
1078 bool halSuccess = true; /* We set this to false to abort the operation. */
1079 autoDBusError dbusError;
1080
1081 RTMemAutoPtr <DBusMessage, VBoxDBusMessageUnref> message, replyFind, replyGet;
1082 RTMemAutoPtr <DBusConnection, VBoxHalShutdown> dbusConnection;
1083 DBusMessageIter iterFind, iterUdis;
1084
1085 rc = halInit (&dbusConnection);
1086 if (!dbusConnection)
1087 halSuccess = false;
1088 if (halSuccess && RT_SUCCESS (rc))
1089 {
1090 rc = halFindDeviceStringMatch (dbusConnection.get(), "storage.drive_type",
1091 isDVD ? "cdrom" : "floppy", &replyFind);
1092 if (!replyFind)
1093 halSuccess = false;
1094 }
1095 if (halSuccess && RT_SUCCESS (rc))
1096 {
1097 dbus_message_iter_init (replyFind.get(), &iterFind);
1098 if (dbus_message_iter_get_arg_type (&iterFind) != DBUS_TYPE_ARRAY)
1099 halSuccess = false;
1100 }
1101 if (halSuccess && RT_SUCCESS (rc))
1102 dbus_message_iter_recurse (&iterFind, &iterUdis);
1103 for (; halSuccess && RT_SUCCESS (rc)
1104 && dbus_message_iter_get_arg_type (&iterUdis) == DBUS_TYPE_STRING;
1105 dbus_message_iter_next(&iterUdis))
1106 {
1107 /* Now get all properties from the iterator */
1108 const char *pszUdi;
1109 dbus_message_iter_get_basic (&iterUdis, &pszUdi);
1110 static const char *papszKeys[] =
1111 { "block.device", "info.product", "info.vendor" };
1112 char *papszValues[RT_ELEMENTS (papszKeys)];
1113 rc = halGetPropertyStrings (dbusConnection.get(), pszUdi, RT_ELEMENTS (papszKeys),
1114 papszKeys, papszValues, &replyGet);
1115 std::string description;
1116 const char *pszDevice = papszValues[0], *pszProduct = papszValues[1],
1117 *pszVendor = papszValues[2];
1118 if (!!replyGet && pszDevice == NULL)
1119 halSuccess = false;
1120 if (!!replyGet && pszDevice != NULL)
1121 {
1122 if ((pszVendor != NULL) && (pszVendor[0] != '\0'))
1123 (description += pszVendor) += " ";
1124 if ((pszProduct != NULL && pszProduct[0] != '\0'))
1125 description += pszProduct;
1126 try
1127 {
1128 pList->push_back (DriveInfo (pszDevice, pszUdi, description));
1129 }
1130 catch (std::bad_alloc)
1131 {
1132 rc = VERR_NO_MEMORY;
1133 }
1134 }
1135 }
1136 if (dbusError.HasName (DBUS_ERROR_NO_MEMORY))
1137 rc = VERR_NO_MEMORY;
1138 if (pfSuccess != NULL)
1139 *pfSuccess = halSuccess;
1140 LogFlow (("rc=%Rrc, halSuccess=%d\n", rc, halSuccess));
1141 dbusError.FlowLog();
1142 return rc;
1143}
1144
1145/**
1146 * Helper function to query the hal subsystem for information about USB devices
1147 * attached to the system.
1148 * @returns iprt status code
1149 * @param pList where to add information about the devices detected
1150 * @param pfSuccess will be set to true if all interactions with hal
1151 * succeeded and to false otherwise. Optional.
1152 *
1153 * @returns IPRT status code
1154 */
1155/* static */
1156int getUSBDeviceInfoFromHal(USBDeviceInfoList *pList, bool *pfSuccess)
1157{
1158 AssertReturn(VALID_PTR (pList) && (pfSuccess == NULL || VALID_PTR (pfSuccess)),
1159 VERR_INVALID_POINTER);
1160 LogFlowFunc (("pList=%p, pfSuccess=%p\n", pList, pfSuccess));
1161 int rc = VINF_SUCCESS; /* We set this to failure on fatal errors. */
1162 bool halSuccess = true; /* We set this to false to abort the operation. */
1163 autoDBusError dbusError;
1164
1165 RTMemAutoPtr <DBusMessage, VBoxDBusMessageUnref> message, replyFind, replyGet;
1166 RTMemAutoPtr <DBusConnection, VBoxHalShutdown> dbusConnection;
1167 DBusMessageIter iterFind, iterUdis;
1168
1169 /* Connect to hal */
1170 rc = halInit (&dbusConnection);
1171 if (!dbusConnection)
1172 halSuccess = false;
1173 /* Get an array of all devices in the usb_device subsystem */
1174 if (halSuccess && RT_SUCCESS (rc))
1175 {
1176 rc = halFindDeviceStringMatch (dbusConnection.get(), "info.subsystem",
1177 "usb_device", &replyFind);
1178 if (!replyFind)
1179 halSuccess = false;
1180 }
1181 if (halSuccess && RT_SUCCESS (rc))
1182 {
1183 dbus_message_iter_init (replyFind.get(), &iterFind);
1184 if (dbus_message_iter_get_arg_type (&iterFind) != DBUS_TYPE_ARRAY)
1185 halSuccess = false;
1186 }
1187 /* Recurse down into the array and query interesting information about the
1188 * entries. */
1189 if (halSuccess && RT_SUCCESS (rc))
1190 dbus_message_iter_recurse (&iterFind, &iterUdis);
1191 for (; halSuccess && RT_SUCCESS (rc)
1192 && dbus_message_iter_get_arg_type (&iterUdis) == DBUS_TYPE_STRING;
1193 dbus_message_iter_next(&iterUdis))
1194 {
1195 /* Get the device node and the sysfs path for the current entry. */
1196 const char *pszUdi;
1197 dbus_message_iter_get_basic (&iterUdis, &pszUdi);
1198 static const char *papszKeys[] = { "linux.device_file", "linux.sysfs_path" };
1199 char *papszValues[RT_ELEMENTS (papszKeys)];
1200 rc = halGetPropertyStrings (dbusConnection.get(), pszUdi, RT_ELEMENTS (papszKeys),
1201 papszKeys, papszValues, &replyGet);
1202 const char *pszDevice = papszValues[0], *pszSysfsPath = papszValues[1];
1203 /* Get the interfaces. */
1204 if (!!replyGet && pszDevice && pszSysfsPath)
1205 {
1206 USBDeviceInfo info (pszDevice, pszSysfsPath);
1207 bool ifaceSuccess = true; /* If we can't get the interfaces, just
1208 * skip this one device. */
1209 rc = getUSBInterfacesFromHal (&info.mInterfaces, pszUdi, &ifaceSuccess);
1210 if (RT_SUCCESS(rc) && halSuccess && ifaceSuccess)
1211 try
1212 {
1213 pList->push_back (info);
1214 }
1215 catch (std::bad_alloc)
1216 {
1217 rc = VERR_NO_MEMORY;
1218 }
1219 }
1220 }
1221 if (dbusError.HasName (DBUS_ERROR_NO_MEMORY))
1222 rc = VERR_NO_MEMORY;
1223 if (pfSuccess != NULL)
1224 *pfSuccess = halSuccess;
1225 LogFlow (("rc=%Rrc, halSuccess=%d\n", rc, halSuccess));
1226 dbusError.FlowLog();
1227 return rc;
1228}
1229
1230/**
1231 * Helper function to query the hal subsystem for information about USB devices
1232 * attached to the system, using the older API.
1233 * @returns iprt status code
1234 * @param pList where to add information about the devices detected
1235 * @param pfSuccess will be set to true if all interactions with hal
1236 * succeeded and to false otherwise. Optional.
1237 *
1238 * @returns IPRT status code
1239 */
1240/* static */
1241int getOldUSBDeviceInfoFromHal(USBDeviceInfoList *pList, bool *pfSuccess)
1242{
1243 AssertReturn(VALID_PTR (pList) && (pfSuccess == NULL || VALID_PTR (pfSuccess)),
1244 VERR_INVALID_POINTER);
1245 LogFlowFunc (("pList=%p, pfSuccess=%p\n", pList, pfSuccess));
1246 int rc = VINF_SUCCESS; /* We set this to failure on fatal errors. */
1247 bool halSuccess = true; /* We set this to false to abort the operation. */
1248 autoDBusError dbusError;
1249
1250 RTMemAutoPtr <DBusMessage, VBoxDBusMessageUnref> message, replyFind, replyGet;
1251 RTMemAutoPtr <DBusConnection, VBoxHalShutdown> dbusConnection;
1252 DBusMessageIter iterFind, iterUdis;
1253
1254 /* Connect to hal */
1255 rc = halInit (&dbusConnection);
1256 if (!dbusConnection)
1257 halSuccess = false;
1258 /* Get an array of all devices in the usb_device subsystem */
1259 if (halSuccess && RT_SUCCESS (rc))
1260 {
1261 rc = halFindDeviceStringMatch (dbusConnection.get(), "info.category",
1262 "usbraw", &replyFind);
1263 if (!replyFind)
1264 halSuccess = false;
1265 }
1266 if (halSuccess && RT_SUCCESS (rc))
1267 {
1268 dbus_message_iter_init (replyFind.get(), &iterFind);
1269 if (dbus_message_iter_get_arg_type (&iterFind) != DBUS_TYPE_ARRAY)
1270 halSuccess = false;
1271 }
1272 /* Recurse down into the array and query interesting information about the
1273 * entries. */
1274 if (halSuccess && RT_SUCCESS (rc))
1275 dbus_message_iter_recurse (&iterFind, &iterUdis);
1276 for (; halSuccess && RT_SUCCESS (rc)
1277 && dbus_message_iter_get_arg_type (&iterUdis) == DBUS_TYPE_STRING;
1278 dbus_message_iter_next(&iterUdis))
1279 {
1280 /* Get the device node and the sysfs path for the current entry. */
1281 const char *pszUdi;
1282 dbus_message_iter_get_basic (&iterUdis, &pszUdi);
1283 static const char *papszKeys[] = { "linux.device_file", "info.parent" };
1284 char *papszValues[RT_ELEMENTS (papszKeys)];
1285 rc = halGetPropertyStrings (dbusConnection.get(), pszUdi, RT_ELEMENTS (papszKeys),
1286 papszKeys, papszValues, &replyGet);
1287 const char *pszDevice = papszValues[0], *pszSysfsPath = papszValues[1];
1288 /* Get the interfaces. */
1289 if (!!replyGet && pszDevice && pszSysfsPath)
1290 {
1291 USBDeviceInfo info (pszDevice, pszSysfsPath);
1292 bool ifaceSuccess = false; /* If we can't get the interfaces, just
1293 * skip this one device. */
1294 rc = getUSBInterfacesFromHal (&info.mInterfaces, pszSysfsPath,
1295 &ifaceSuccess);
1296 if (RT_SUCCESS(rc) && halSuccess && ifaceSuccess)
1297 try
1298 {
1299 pList->push_back (info);
1300 }
1301 catch (std::bad_alloc)
1302 {
1303 rc = VERR_NO_MEMORY;
1304 }
1305 }
1306 }
1307 if (dbusError.HasName (DBUS_ERROR_NO_MEMORY))
1308 rc = VERR_NO_MEMORY;
1309 if (pfSuccess != NULL)
1310 *pfSuccess = halSuccess;
1311 LogFlow (("rc=%Rrc, halSuccess=%d\n", rc, halSuccess));
1312 dbusError.FlowLog();
1313 return rc;
1314}
1315
1316/**
1317 * Helper function to query the hal subsystem for information about USB devices
1318 * attached to the system.
1319 * @returns iprt status code
1320 * @param pList where to add information about the devices detected. If
1321 * certain interfaces are not found (@a pfFound is false on
1322 * return) this may contain invalid information.
1323 * @param pcszUdi the hal UDI of the device
1324 * @param pfSuccess will be set to true if the operation succeeds and to
1325 * false if it fails for non-critical reasons. Optional.
1326 *
1327 * @returns IPRT status code
1328 */
1329/* static */
1330int getUSBInterfacesFromHal(std::vector <std::string> *pList,
1331 const char *pcszUdi, bool *pfSuccess)
1332{
1333 AssertReturn(VALID_PTR (pList) && VALID_PTR (pcszUdi) &&
1334 (pfSuccess == NULL || VALID_PTR (pfSuccess)),
1335 VERR_INVALID_POINTER);
1336 LogFlowFunc (("pList=%p, pcszUdi=%s, pfSuccess=%p\n", pList, pcszUdi,
1337 pfSuccess));
1338 int rc = VINF_SUCCESS; /* We set this to failure on fatal errors. */
1339 bool halSuccess = true; /* We set this to false to abort the operation. */
1340 autoDBusError dbusError;
1341
1342 RTMemAutoPtr <DBusMessage, VBoxDBusMessageUnref> message, replyFind, replyGet;
1343 RTMemAutoPtr <DBusConnection, VBoxHalShutdown> dbusConnection;
1344 DBusMessageIter iterFind, iterUdis;
1345
1346 rc = halInit (&dbusConnection);
1347 if (!dbusConnection)
1348 halSuccess = false;
1349 if (halSuccess && RT_SUCCESS (rc))
1350 {
1351 /* Look for children of the current UDI. */
1352 rc = halFindDeviceStringMatch (dbusConnection.get(), "info.parent",
1353 pcszUdi, &replyFind);
1354 if (!replyFind)
1355 halSuccess = false;
1356 }
1357 if (halSuccess && RT_SUCCESS (rc))
1358 {
1359 dbus_message_iter_init (replyFind.get(), &iterFind);
1360 if (dbus_message_iter_get_arg_type (&iterFind) != DBUS_TYPE_ARRAY)
1361 halSuccess = false;
1362 }
1363 if (halSuccess && RT_SUCCESS (rc))
1364 dbus_message_iter_recurse (&iterFind, &iterUdis);
1365 for (; halSuccess && RT_SUCCESS (rc)
1366 && dbus_message_iter_get_arg_type (&iterUdis) == DBUS_TYPE_STRING;
1367 dbus_message_iter_next(&iterUdis))
1368 {
1369 /* Now get the sysfs path and the subsystem from the iterator */
1370 const char *pszUdi;
1371 dbus_message_iter_get_basic (&iterUdis, &pszUdi);
1372 static const char *papszKeys[] = { "linux.sysfs_path", "info.subsystem",
1373 "linux.subsystem" };
1374 char *papszValues[RT_ELEMENTS (papszKeys)];
1375 rc = halGetPropertyStrings (dbusConnection.get(), pszUdi, RT_ELEMENTS (papszKeys),
1376 papszKeys, papszValues, &replyGet);
1377 const char *pszSysfsPath = papszValues[0], *pszInfoSubsystem = papszValues[1],
1378 *pszLinuxSubsystem = papszValues[2];
1379 if (!replyGet)
1380 halSuccess = false;
1381 if (!!replyGet && pszSysfsPath == NULL)
1382 halSuccess = false;
1383 if ( halSuccess && RT_SUCCESS (rc)
1384 && RTStrCmp (pszInfoSubsystem, "usb_device") != 0 /* Children of buses can also be devices. */
1385 && RTStrCmp (pszLinuxSubsystem, "usb_device") != 0)
1386 try
1387 {
1388 pList->push_back (pszSysfsPath);
1389 }
1390 catch (std::bad_alloc)
1391 {
1392 rc = VERR_NO_MEMORY;
1393 }
1394 }
1395 if (dbusError.HasName (DBUS_ERROR_NO_MEMORY))
1396 rc = VERR_NO_MEMORY;
1397 if (pfSuccess != NULL)
1398 *pfSuccess = halSuccess;
1399 LogFlow (("rc=%Rrc, halSuccess=%d\n", rc, halSuccess));
1400 dbusError.FlowLog();
1401 return rc;
1402}
1403
1404/**
1405 * When it is registered with DBus, this function will be called by
1406 * dbus_connection_read_write_dispatch each time a message is received over the
1407 * DBus connection. We check whether that message was caused by a hal device
1408 * hotplug event, and if so we set a flag. dbus_connection_read_write_dispatch
1409 * will return after calling its filter functions, and its caller should then
1410 * check the status of the flag passed to the filter function.
1411 *
1412 * @param pConnection The DBus connection we are using.
1413 * @param pMessage The DBus message which just arrived.
1414 * @param pvUser A pointer to the flag variable we are to set.
1415 */
1416/* static */
1417DBusHandlerResult dbusFilterFunction (DBusConnection * /* pConnection */,
1418 DBusMessage *pMessage, void *pvUser)
1419{
1420 volatile bool *pTriggered = reinterpret_cast<volatile bool *> (pvUser);
1421 if ( dbus_message_is_signal (pMessage, "org.freedesktop.Hal.Manager",
1422 "DeviceAdded")
1423 || dbus_message_is_signal (pMessage, "org.freedesktop.Hal.Manager",
1424 "DeviceRemoved"))
1425 {
1426 *pTriggered = true;
1427 }
1428 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1429}
1430#endif /* RT_OS_LINUX && VBOX_WITH_DBUS */
1431
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