VirtualBox

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

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

Main/USBProxyServiceLinux: logging and fix a burn

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