VirtualBox

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

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

Main/Linux: fixes and clean ups in the DBus code

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 47.1 KB
Line 
1/* $Id: HostHardwareLinux.cpp 16178 2009-01-22 15:39:48Z 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 bool mTriggered;
233 /** A flag to say that we wish to interrupt the current wait. */
234 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 &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 &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 (cMillies != RT_INDEFINITE_WAIT)
307 mContext->mInterrupt = true;
308 }
309 if (!connected)
310 rc = VERR_TRY_AGAIN;
311#else /* !(defined RT_OS_LINUX && defined VBOX_WITH_DBUS) */
312 rc = VERR_NOT_IMPLEMENTED;
313#endif /* !(defined RT_OS_LINUX && defined VBOX_WITH_DBUS) */
314 return rc;
315}
316
317/* Set a flag to tell the Wait not to resume next time it times out. */
318void VBoxMainHotplugWaiter::Interrupt()
319{
320#if defined RT_OS_LINUX && defined VBOX_WITH_DBUS
321 mContext->mInterrupt = true;
322#endif /* defined RT_OS_LINUX && defined VBOX_WITH_DBUS */
323}
324
325#ifdef RT_OS_LINUX
326/**
327 * Helper function to check whether the given device node is a valid drive
328 */
329/* static */
330bool validateDevice(const char *deviceNode, bool isDVD)
331{
332 AssertReturn(VALID_PTR (deviceNode), VERR_INVALID_POINTER);
333 LogFlowFunc (("deviceNode=%s, isDVD=%d\n", deviceNode, isDVD));
334 struct stat statInfo;
335 bool retValue = false;
336
337 // sanity check
338 if (!deviceNode)
339 {
340 return false;
341 }
342
343 // first a simple stat() call
344 if (stat(deviceNode, &statInfo) < 0)
345 {
346 return false;
347 } else
348 {
349 if (isDVD)
350 {
351 if (S_ISCHR(statInfo.st_mode) || S_ISBLK(statInfo.st_mode))
352 {
353 int fileHandle;
354 // now try to open the device
355 fileHandle = open(deviceNode, O_RDONLY | O_NONBLOCK, 0);
356 if (fileHandle >= 0)
357 {
358 cdrom_subchnl cdChannelInfo;
359 cdChannelInfo.cdsc_format = CDROM_MSF;
360 // this call will finally reveal the whole truth
361#ifdef RT_OS_LINUX
362 if ((ioctl(fileHandle, CDROMSUBCHNL, &cdChannelInfo) == 0) ||
363 (errno == EIO) || (errno == ENOENT) ||
364 (errno == EINVAL) || (errno == ENOMEDIUM))
365#endif
366 {
367 retValue = true;
368 }
369 close(fileHandle);
370 }
371 }
372 } else
373 {
374 // floppy case
375 if (S_ISCHR(statInfo.st_mode) || S_ISBLK(statInfo.st_mode))
376 {
377 /// @todo do some more testing, maybe a nice IOCTL!
378 retValue = true;
379 }
380 }
381 }
382 LogFlowFunc (("retValue=%d\n", retValue));
383 return retValue;
384}
385#else /* !RT_OS_LINUX */
386# error Port me! Copying code over from HostImpl.cpp should be most of the job though.
387#endif /* !RT_OS_LINUX */
388
389/**
390 * Extract the names of drives from an environment variable and add them to a
391 * list if they are valid.
392 * @returns iprt status code
393 * @param pszVar the name of the environment variable. The variable
394 * value should be a list of device node names, separated
395 * by ':' characters.
396 * @param pList the list to append the drives found to
397 * @param isDVD are we looking for DVD drives or for floppies?
398 * @param pfSuccess this will be set to true if we found at least one drive
399 * and to false otherwise. Optional.
400 */
401/* static */
402int getDriveInfoFromEnv(const char *pszVar, DriveInfoList *pList,
403 bool isDVD, bool *pfSuccess)
404{
405 AssertReturn( VALID_PTR (pszVar) && VALID_PTR (pList)
406 && (pfSuccess == NULL || VALID_PTR (pfSuccess)),
407 VERR_INVALID_POINTER);
408 LogFlowFunc (("pszVar=%s, pList=%p, isDVD=%d, pfSuccess=%p\n", pszVar,
409 pList, isDVD, pfSuccess));
410 int rc = VINF_SUCCESS;
411 bool success = false;
412 RTMemAutoPtr<char, RTStrFree> drive;
413 const char *pszValue = RTEnvGet (pszVar);
414 if (pszValue != NULL)
415 {
416 drive = RTStrDup (pszValue);
417 if (!drive)
418 rc = VERR_NO_MEMORY;
419 }
420 if (pszValue != NULL && RT_SUCCESS (rc))
421 {
422 char *pDrive = drive.get();
423 char *pDriveNext = strchr (pDrive, ':');
424 while (pDrive != NULL && *pDrive != '\0')
425 {
426 if (pDriveNext != NULL)
427 *pDriveNext = '\0';
428 if (validateDevice(pDrive, isDVD))
429 {
430 pList->push_back (DriveInfo (pDrive));
431 success = true;
432 }
433 if (pDriveNext != NULL)
434 {
435 pDrive = pDriveNext + 1;
436 pDriveNext = strchr (pDrive, ':');
437 }
438 else
439 pDrive = NULL;
440 }
441 }
442 if (pfSuccess != NULL)
443 *pfSuccess = success;
444 LogFlowFunc (("rc=%Rrc, success=%d\n", rc, success));
445 return rc;
446}
447
448#ifdef RT_OS_LINUX
449/**
450 * Helper function to parse the given mount file and add found entries
451 */
452/* static */
453int getDVDInfoFromMTab(char *mountTable, DriveInfoList *pList)
454{
455 AssertReturn(VALID_PTR (mountTable) && VALID_PTR (pList),
456 VERR_INVALID_POINTER);
457#ifdef RT_OS_LINUX
458 LogFlowFunc (("mountTable=%s, pList=%p\n", mountTable, pList));
459 int rc = VINF_SUCCESS;
460 FILE *mtab = setmntent(mountTable, "r");
461 if (mtab)
462 {
463 struct mntent *mntent;
464 RTMemAutoPtr <char, RTStrFree> mnt_type, mnt_dev;
465 char *tmp;
466 while (RT_SUCCESS (rc) && (mntent = getmntent(mtab)))
467 {
468 mnt_type = RTStrDup (mntent->mnt_type);
469 mnt_dev = RTStrDup (mntent->mnt_fsname);
470 if (!mnt_type || !mnt_dev)
471 rc = VERR_NO_MEMORY;
472 // supermount fs case
473 if (RT_SUCCESS (rc) && strcmp(mnt_type.get(), "supermount") == 0)
474 {
475 tmp = strstr(mntent->mnt_opts, "fs=");
476 if (tmp)
477 {
478 mnt_type = RTStrDup(tmp + strlen("fs="));
479 if (!mnt_type)
480 rc = VERR_NO_MEMORY;
481 else
482 {
483 tmp = strchr(mnt_type.get(), ',');
484 if (tmp)
485 *tmp = '\0';
486 }
487 }
488 tmp = strstr(mntent->mnt_opts, "dev=");
489 if (tmp)
490 {
491 mnt_dev = RTStrDup(tmp + strlen("dev="));
492 if (!mnt_dev)
493 rc = VERR_NO_MEMORY;
494 else
495 {
496 tmp = strchr(mnt_dev.get(), ',');
497 if (tmp)
498 *tmp = '\0';
499 }
500 }
501 }
502 // use strstr here to cover things fs types like "udf,iso9660"
503 if (RT_SUCCESS (rc) && strstr(mnt_type.get(), "iso9660") == 0)
504 {
505 if (validateDevice(mnt_dev.get(), true))
506 {
507 bool insert = true;
508 struct stat srcInfo;
509 if (stat (mnt_dev.get(), &srcInfo) < 0)
510 insert = false;
511 for (DriveInfoList::const_iterator it = pList->begin();
512 insert && it != pList->end(); ++it)
513 {
514 struct stat destInfo;
515 if ( (stat (it->mDevice.c_str(), &destInfo) == 0)
516 && (srcInfo.st_rdev == destInfo.st_rdev))
517 insert = false;
518 }
519 if (insert)
520 pList->push_back (DriveInfo (mnt_dev.get()));
521 }
522 }
523 }
524 endmntent(mtab);
525 }
526 return rc;
527#endif
528}
529
530#endif /* RT_OS_LINUX */
531
532#if defined(RT_OS_LINUX) && defined(VBOX_WITH_DBUS)
533/** Wrapper class around DBusError for automatic cleanup */
534class autoDBusError
535{
536 DBusError mError;
537public:
538 autoDBusError () { dbus_error_init (&mError); }
539 ~autoDBusError ()
540 {
541 if (IsSet())
542 dbus_error_free (&mError);
543 }
544 DBusError &get () { return mError; }
545 bool IsSet ()
546 {
547 Assert ((mError.name == NULL) == (mError.message == NULL));
548 return (mError.name != NULL);
549 }
550 bool HasName (const char *pszName)
551 {
552 Assert ((mError.name == NULL) == (mError.message == NULL));
553 return (RTStrCmp (mError.name, pszName) == 0);
554 }
555 void FlowLog ()
556 {
557 if (IsSet ())
558 LogFlow(("DBus error %s: %s\n", mError.name, mError.message));
559 }
560};
561
562/**
563 * Helper function for setting up a connection to the DBus daemon and
564 * registering with the hal service.
565 *
566 * @note If libdbus is being loaded at runtime then be sure to call
567 * VBoxDBusCheckPresence before calling this.
568 * @returns iprt status code
569 * @param ppConnection where to store the connection handle
570 */
571/* static */
572int halInit (RTMemAutoPtr <DBusConnection, VBoxHalShutdown> *pConnection)
573{
574 AssertReturn(VALID_PTR (pConnection), VERR_INVALID_POINTER);
575 LogFlowFunc (("pConnection=%p\n", pConnection));
576 int rc = VINF_SUCCESS;
577 bool halSuccess = true;
578 autoDBusError dbusError;
579
580 RTMemAutoPtr <DBusConnection, VBoxDBusConnectionUnref> dbusConnection;
581 dbusConnection = dbus_bus_get (DBUS_BUS_SYSTEM, &dbusError.get());
582 if (!dbusConnection)
583 halSuccess = false;
584 if (halSuccess)
585 {
586 dbus_connection_set_exit_on_disconnect (dbusConnection.get(), false);
587 halSuccess = dbus_bus_name_has_owner (dbusConnection.get(),
588 "org.freedesktop.Hal", &dbusError.get());
589 }
590 if (halSuccess)
591 {
592 dbus_bus_add_match (dbusConnection.get(),
593 "type='signal',"
594 "interface='org.freedesktop.Hal.Manager',"
595 "sender='org.freedesktop.Hal',"
596 "path='/org/freedesktop/Hal/Manager'",
597 &dbusError.get());
598 halSuccess = !dbusError.IsSet();
599 }
600 if (dbusError.HasName (DBUS_ERROR_NO_MEMORY))
601 rc = VERR_NO_MEMORY;
602 if (halSuccess)
603 *pConnection = dbusConnection.release();
604 LogFlowFunc(("rc=%Rrc, (*pConnection).get()=%p\n", rc, (*pConnection).get()));
605 dbusError.FlowLog();
606 return rc;
607}
608
609/**
610 * Helper function for setting up a private connection to the DBus daemon and
611 * registering with the hal service. Private connections are considered
612 * unsociable and should not be used unnecessarily (as per the DBus API docs).
613 *
614 * @note If libdbus is being loaded at runtime then be sure to call
615 * VBoxDBusCheckPresence before calling this.
616 * @returns iprt status code
617 * @param pConnection where to store the connection handle
618 */
619/* static */
620int halInitPrivate (RTMemAutoPtr <DBusConnection, VBoxHalShutdownPrivate> *pConnection)
621{
622 AssertReturn(VALID_PTR (pConnection), VERR_INVALID_POINTER);
623 LogFlowFunc (("pConnection=%p\n", pConnection));
624 int rc = VINF_SUCCESS;
625 bool halSuccess = true;
626 autoDBusError dbusError;
627
628 RTMemAutoPtr <DBusConnection, VBoxDBusConnectionCloseAndUnref> dbusConnection;
629 dbusConnection = dbus_bus_get_private (DBUS_BUS_SYSTEM, &dbusError.get());
630 if (!dbusConnection)
631 halSuccess = false;
632 if (halSuccess)
633 {
634 dbus_connection_set_exit_on_disconnect (dbusConnection.get(), false);
635 halSuccess = dbus_bus_name_has_owner (dbusConnection.get(),
636 "org.freedesktop.Hal", &dbusError.get());
637 }
638 if (halSuccess)
639 {
640 dbus_bus_add_match (dbusConnection.get(),
641 "type='signal',"
642 "interface='org.freedesktop.Hal.Manager',"
643 "sender='org.freedesktop.Hal',"
644 "path='/org/freedesktop/Hal/Manager'",
645 &dbusError.get());
646 halSuccess = !dbusError.IsSet();
647 }
648 if (dbusError.HasName (DBUS_ERROR_NO_MEMORY))
649 rc = VERR_NO_MEMORY;
650 if (halSuccess)
651 *pConnection = dbusConnection.release();
652 LogFlowFunc(("rc=%Rrc, (*pConnection).get()=%p\n", rc, (*pConnection).get()));
653 dbusError.FlowLog();
654 return rc;
655}
656
657/**
658 * Helper function for shutting down a connection to DBus and hal.
659 * @param pConnection the connection handle
660 */
661/* extern */
662void VBoxHalShutdown (DBusConnection *pConnection)
663{
664 AssertReturnVoid(VALID_PTR (pConnection));
665 LogFlowFunc (("pConnection=%p\n", pConnection));
666 autoDBusError dbusError;
667
668 dbus_bus_remove_match (pConnection,
669 "type='signal',"
670 "interface='org.freedesktop.Hal.Manager',"
671 "sender='org.freedesktop.Hal',"
672 "path='/org/freedesktop/Hal/Manager'",
673 &dbusError.get());
674 dbus_connection_unref (pConnection);
675 LogFlowFunc(("returning\n"));
676 dbusError.FlowLog();
677}
678
679/**
680 * Helper function for shutting down a private connection to DBus and hal.
681 * @param pConnection the connection handle
682 */
683/* extern */
684void VBoxHalShutdownPrivate (DBusConnection *pConnection)
685{
686 AssertReturnVoid(VALID_PTR (pConnection));
687 LogFlowFunc (("pConnection=%p\n", pConnection));
688 autoDBusError dbusError;
689
690 dbus_bus_remove_match (pConnection,
691 "type='signal',"
692 "interface='org.freedesktop.Hal.Manager',"
693 "sender='org.freedesktop.Hal',"
694 "path='/org/freedesktop/Hal/Manager'",
695 &dbusError.get());
696 dbus_connection_close (pConnection);
697 dbus_connection_unref (pConnection);
698 LogFlowFunc(("returning\n"));
699 dbusError.FlowLog();
700}
701
702/** Wrapper around dbus_connection_unref. We need this to use it as a real
703 * function in auto pointers, as a function pointer won't wash here. */
704/* extern */
705void VBoxDBusConnectionUnref(DBusConnection *pConnection)
706{
707 dbus_connection_unref(pConnection);
708}
709
710/**
711 * This function closes and unrefs a private connection to dbus. It should
712 * only be called once no-one else is referencing the connection.
713 */
714/* extern */
715void VBoxDBusConnectionCloseAndUnref(DBusConnection *pConnection)
716{
717 dbus_connection_close(pConnection);
718 dbus_connection_unref(pConnection);
719}
720
721/** Wrapper around dbus_message_unref. We need this to use it as a real
722 * function in auto pointers, as a function pointer won't wash here. */
723/* extern */
724void VBoxDBusMessageUnref(DBusMessage *pMessage)
725{
726 dbus_message_unref(pMessage);
727}
728
729/**
730 * Find the UDIs of hal entries that contain Key=Value property.
731 * @returns iprt status code. If a non-fatal error occurs, we return success
732 * but reset pMessage to NULL.
733 * @param pConnection an initialised connection DBus
734 * @param pszKey the property key
735 * @param pszValue the property value
736 * @param pMessage where to store the return DBus message. This must be
737 * parsed to get at the UDIs. NOT optional.
738 */
739/* static */
740int halFindDeviceStringMatch (DBusConnection *pConnection, const char *pszKey,
741 const char *pszValue,
742 RTMemAutoPtr <DBusMessage, VBoxDBusMessageUnref> *pMessage)
743{
744 AssertReturn( VALID_PTR (pConnection) && VALID_PTR (pszKey)
745 && VALID_PTR (pszValue) && VALID_PTR (pMessage),
746 VERR_INVALID_POINTER);
747 LogFlowFunc (("pConnection=%p, pszKey=%s, pszValue=%s, pMessage=%p\n",
748 pConnection, pszKey, pszValue, pMessage));
749 int rc = VINF_SUCCESS; /* We set this to failure on fatal errors. */
750 bool halSuccess = true; /* We set this to false to abort the operation. */
751 autoDBusError dbusError;
752
753 RTMemAutoPtr <DBusMessage, VBoxDBusMessageUnref> message, reply;
754 if (halSuccess && RT_SUCCESS (rc))
755 {
756 message = dbus_message_new_method_call ("org.freedesktop.Hal",
757 "/org/freedesktop/Hal/Manager",
758 "org.freedesktop.Hal.Manager",
759 "FindDeviceStringMatch");
760 if (!message)
761 rc = VERR_NO_MEMORY;
762 }
763 if (halSuccess && RT_SUCCESS (rc))
764 {
765 DBusMessageIter iterAppend;
766 dbus_message_iter_init_append (message.get(), &iterAppend);
767 dbus_message_iter_append_basic (&iterAppend, DBUS_TYPE_STRING, &pszKey);
768 dbus_message_iter_append_basic (&iterAppend, DBUS_TYPE_STRING, &pszValue);
769 reply = dbus_connection_send_with_reply_and_block (pConnection,
770 message.get(), -1,
771 &dbusError.get());
772 if (!reply)
773 halSuccess = false;
774 }
775 *pMessage = reply.release ();
776 LogFlowFunc (("rc=%Rrc, *pMessage.value()=%p\n", rc, (*pMessage).get()));
777 dbusError.FlowLog();
778 return rc;
779}
780
781/**
782 * Read a set of string properties for a device. If some of the properties are
783 * not of type DBUS_TYPE_STRING then a NULL pointer will be returned for them.
784 * @returns iprt status code. If the operation failed for non-fatal reasons
785 * then we return success and leave pMessage untouched - reset it
786 * before the call to detect this.
787 * @param pConnection an initialised connection DBus
788 * @param pszUdi the Udi of the device
789 * @param cProps the number of property values to look up
790 * @param papszKeys the keys of the properties to be looked up
791 * @param papszValues where to store the values of the properties. The
792 * strings returned will be valid until the message
793 * returned in @a ppMessage is freed. Undefined if
794 * the message is NULL.
795 * @param pMessage where to store the return DBus message. The caller
796 * is responsible for freeing this once they have
797 * finished with the value strings. NOT optional.
798 */
799/* static */
800int halGetPropertyStrings (DBusConnection *pConnection, const char *pszUdi,
801 size_t cProps, const char **papszKeys,
802 char **papszValues,
803 RTMemAutoPtr <DBusMessage, VBoxDBusMessageUnref> *pMessage)
804{
805 AssertReturn( VALID_PTR (pConnection) && VALID_PTR (pszUdi)
806 && VALID_PTR (papszKeys) && VALID_PTR (papszValues)
807 && VALID_PTR (pMessage),
808 VERR_INVALID_POINTER);
809 LogFlowFunc (("pConnection=%p, pszUdi=%s, cProps=%llu, papszKeys=%p, papszValues=%p, pMessage=%p\n",
810 pConnection, pszUdi, cProps, papszKeys, papszValues, pMessage));
811 int rc = VINF_SUCCESS; /* We set this to failure on fatal errors. */
812 bool halSuccess = true; /* We set this to false to abort the operation. */
813 autoDBusError dbusError;
814
815 RTMemAutoPtr <DBusMessage, VBoxDBusMessageUnref> message, reply;
816 DBusMessageIter iterGet, iterProps, iterKey, iterValue;
817
818 /* Initialise the return array to NULLs */
819 for (size_t i = 0; i < cProps; ++i)
820 papszValues[i] = NULL;
821
822 /* Send a GetAllProperties message to hald */
823 message = dbus_message_new_method_call ("org.freedesktop.Hal", pszUdi,
824 "org.freedesktop.Hal.Device",
825 "GetAllProperties");
826 if (!message)
827 rc = VERR_NO_MEMORY;
828 if (halSuccess && RT_SUCCESS (rc))
829 {
830 reply = dbus_connection_send_with_reply_and_block (pConnection,
831 message.get(), -1,
832 &dbusError.get());
833 if (!reply)
834 halSuccess = false;
835 }
836
837 /* Parse the reply */
838 if (halSuccess && RT_SUCCESS (rc))
839 {
840 dbus_message_iter_init (reply.get(), &iterGet);
841 if ( dbus_message_iter_get_arg_type (&iterGet) != DBUS_TYPE_ARRAY
842 && dbus_message_iter_get_element_type (&iterGet) != DBUS_TYPE_DICT_ENTRY)
843 halSuccess = false;
844 }
845 if (halSuccess && RT_SUCCESS (rc))
846 dbus_message_iter_recurse (&iterGet, &iterProps);
847 /* Go through all entries in the reply and see if any match our keys. */
848 while ( halSuccess && RT_SUCCESS (rc)
849 && dbus_message_iter_get_arg_type (&iterProps)
850 == DBUS_TYPE_DICT_ENTRY)
851 {
852 const char *pszKey;
853 DBusMessageIter iterEntry, iterValue;
854 dbus_message_iter_recurse (&iterProps, &iterEntry);
855 dbus_message_iter_get_basic (&iterEntry, &pszKey);
856 dbus_message_iter_next (&iterEntry);
857 dbus_message_iter_recurse (&iterEntry, &iterValue);
858 /* Fill in any matches. */
859 for (size_t i = 0; i < cProps; ++i)
860 if (strcmp (pszKey, papszKeys[i]) == 0)
861 {
862 if (dbus_message_iter_get_arg_type (&iterValue) == DBUS_TYPE_STRING)
863 dbus_message_iter_get_basic (&iterValue, &papszValues[i]);
864 }
865 dbus_message_iter_next (&iterProps);
866 }
867 if (RT_SUCCESS (rc) && halSuccess)
868 *pMessage = reply.release();
869 if (dbusError.HasName (DBUS_ERROR_NO_MEMORY))
870 rc = VERR_NO_MEMORY;
871 LogFlowFunc (("rc=%Rrc, *pMessage.value()=%p\n", rc, (*pMessage).get()));
872 dbusError.FlowLog();
873 return rc;
874}
875
876/**
877 * Helper function to query the hal subsystem for information about drives
878 * attached to the system.
879 * @returns iprt status code
880 * @param pList where to add information about the drives detected
881 * @param isDVD are we looking for DVDs or floppies?
882 * @param pfSuccess will be set to true if all interactions with hal
883 * succeeded and to false otherwise. Optional.
884 *
885 * @returns IPRT status code
886 */
887/* static */
888int getDriveInfoFromHal(DriveInfoList *pList, bool isDVD, bool *pfSuccess)
889{
890 AssertReturn(VALID_PTR (pList) && (pfSuccess == NULL || VALID_PTR (pfSuccess)),
891 VERR_INVALID_POINTER);
892 LogFlowFunc (("pList=%p, isDVD=%d, pfSuccess=%p\n", pList, isDVD, pfSuccess));
893 int rc = VINF_SUCCESS; /* We set this to failure on fatal errors. */
894 bool halSuccess = true; /* We set this to false to abort the operation. */
895 autoDBusError dbusError;
896
897 RTMemAutoPtr <DBusMessage, VBoxDBusMessageUnref> message, replyFind, replyGet;
898 RTMemAutoPtr <DBusConnection, VBoxHalShutdown> dbusConnection;
899 DBusMessageIter iterFind, iterUdis;
900
901 rc = halInit (&dbusConnection);
902 if (!dbusConnection)
903 halSuccess = false;
904 if (halSuccess && RT_SUCCESS (rc))
905 {
906 rc = halFindDeviceStringMatch (dbusConnection.get(), "storage.drive_type",
907 isDVD ? "cdrom" : "floppy", &replyFind);
908 if (!replyFind)
909 halSuccess = false;
910 }
911 if (halSuccess && RT_SUCCESS (rc))
912 {
913 dbus_message_iter_init (replyFind.get(), &iterFind);
914 if (dbus_message_iter_get_arg_type (&iterFind) != DBUS_TYPE_ARRAY)
915 halSuccess = false;
916 }
917 if (halSuccess && RT_SUCCESS (rc))
918 dbus_message_iter_recurse (&iterFind, &iterUdis);
919 for (; halSuccess && RT_SUCCESS (rc)
920 && dbus_message_iter_get_arg_type (&iterUdis) == DBUS_TYPE_STRING;
921 dbus_message_iter_next(&iterUdis))
922 {
923 /* Now get all properties from the iterator */
924 const char *pszUdi;
925 dbus_message_iter_get_basic (&iterUdis, &pszUdi);
926 static const char *papszKeys[] =
927 { "block.device", "info.product", "info.vendor" };
928 char *papszValues[RT_ELEMENTS (papszKeys)];
929 rc = halGetPropertyStrings (dbusConnection.get(), pszUdi, RT_ELEMENTS (papszKeys),
930 papszKeys, papszValues, &replyGet);
931 std::string description;
932 const char *pszDevice = papszValues[0], *pszProduct = papszValues[1],
933 *pszVendor = papszValues[2];
934 if (!!replyGet && pszDevice == NULL)
935 halSuccess = false;
936 if (!!replyGet && pszDevice != NULL)
937 {
938 if ((pszVendor != NULL) && (pszVendor[0] != '\0'))
939 (description += pszVendor) += " ";
940 if ((pszProduct != NULL && pszProduct[0] != '\0'))
941 description += pszProduct;
942 pList->push_back (DriveInfo (pszDevice, pszUdi, description));
943 }
944 }
945 if (dbusError.HasName (DBUS_ERROR_NO_MEMORY))
946 rc = VERR_NO_MEMORY;
947 if (pfSuccess != NULL)
948 *pfSuccess = halSuccess;
949 LogFlow (("rc=%Rrc, halSuccess=%d\n", rc, halSuccess));
950 dbusError.FlowLog();
951 return rc;
952}
953
954/**
955 * Helper function to query the hal subsystem for information about USB devices
956 * attached to the system.
957 * @returns iprt status code
958 * @param pList where to add information about the devices detected
959 * @param pfSuccess will be set to true if all interactions with hal
960 * succeeded and to false otherwise. Optional.
961 *
962 * @returns IPRT status code
963 */
964/* static */
965int getUSBDeviceInfoFromHal(USBDeviceInfoList *pList, bool *pfSuccess)
966{
967 AssertReturn(VALID_PTR (pList) && (pfSuccess == NULL || VALID_PTR (pfSuccess)),
968 VERR_INVALID_POINTER);
969 LogFlowFunc (("pList=%p, pfSuccess=%p\n", pList, pfSuccess));
970 int rc = VINF_SUCCESS; /* We set this to failure on fatal errors. */
971 bool halSuccess = true; /* We set this to false to abort the operation. */
972 autoDBusError dbusError;
973
974 RTMemAutoPtr <DBusMessage, VBoxDBusMessageUnref> message, replyFind, replyGet;
975 RTMemAutoPtr <DBusConnection, VBoxHalShutdown> dbusConnection;
976 DBusMessageIter iterFind, iterUdis;
977
978 /* Connect to hal */
979 rc = halInit (&dbusConnection);
980 if (!dbusConnection)
981 halSuccess = false;
982 /* Get an array of all devices in the usb_device subsystem */
983 if (halSuccess && RT_SUCCESS (rc))
984 {
985 rc = halFindDeviceStringMatch (dbusConnection.get(), "info.subsystem",
986 "usb_device", &replyFind);
987 if (!replyFind)
988 halSuccess = false;
989 }
990 if (halSuccess && RT_SUCCESS (rc))
991 {
992 dbus_message_iter_init (replyFind.get(), &iterFind);
993 if (dbus_message_iter_get_arg_type (&iterFind) != DBUS_TYPE_ARRAY)
994 halSuccess = false;
995 }
996 /* Recurse down into the array and query interesting information about the
997 * entries. */
998 if (halSuccess && RT_SUCCESS (rc))
999 dbus_message_iter_recurse (&iterFind, &iterUdis);
1000 for (; halSuccess && RT_SUCCESS (rc)
1001 && dbus_message_iter_get_arg_type (&iterUdis) == DBUS_TYPE_STRING;
1002 dbus_message_iter_next(&iterUdis))
1003 {
1004 /* Get the device node and the sysfs path for the current entry. */
1005 const char *pszUdi;
1006 dbus_message_iter_get_basic (&iterUdis, &pszUdi);
1007 static const char *papszKeys[] = { "linux.device_file", "linux.sysfs_path" };
1008 char *papszValues[RT_ELEMENTS (papszKeys)];
1009 rc = halGetPropertyStrings (dbusConnection.get(), pszUdi, RT_ELEMENTS (papszKeys),
1010 papszKeys, papszValues, &replyGet);
1011 const char *pszDevice = papszValues[0], *pszSysfsPath = papszValues[1];
1012 /* Get the interfaces. */
1013 if (!!replyGet && pszDevice && pszSysfsPath)
1014 {
1015 USBDeviceInfo info (pszDevice, pszSysfsPath);
1016 bool ifaceSuccess = true; /* If we can't get the interfaces, just
1017 * skip this one device. */
1018 rc = getUSBInterfacesFromHal (&info.mInterfaces, pszUdi, &ifaceSuccess);
1019 if (RT_SUCCESS(rc) && halSuccess && ifaceSuccess)
1020 pList->push_back (info);
1021 }
1022 }
1023 if (dbusError.HasName (DBUS_ERROR_NO_MEMORY))
1024 rc = VERR_NO_MEMORY;
1025 if (pfSuccess != NULL)
1026 *pfSuccess = halSuccess;
1027 LogFlow (("rc=%Rrc, halSuccess=%d\n", rc, halSuccess));
1028 dbusError.FlowLog();
1029 return rc;
1030}
1031
1032/**
1033 * Helper function to query the hal subsystem for information about USB devices
1034 * attached to the system, using the older API.
1035 * @returns iprt status code
1036 * @param pList where to add information about the devices detected
1037 * @param pfSuccess will be set to true if all interactions with hal
1038 * succeeded and to false otherwise. Optional.
1039 *
1040 * @returns IPRT status code
1041 */
1042/* static */
1043int getOldUSBDeviceInfoFromHal(USBDeviceInfoList *pList, bool *pfSuccess)
1044{
1045 AssertReturn(VALID_PTR (pList) && (pfSuccess == NULL || VALID_PTR (pfSuccess)),
1046 VERR_INVALID_POINTER);
1047 LogFlowFunc (("pList=%p, pfSuccess=%p\n", pList, pfSuccess));
1048 int rc = VINF_SUCCESS; /* We set this to failure on fatal errors. */
1049 bool halSuccess = true; /* We set this to false to abort the operation. */
1050 autoDBusError dbusError;
1051
1052 RTMemAutoPtr <DBusMessage, VBoxDBusMessageUnref> message, replyFind, replyGet;
1053 RTMemAutoPtr <DBusConnection, VBoxHalShutdown> dbusConnection;
1054 DBusMessageIter iterFind, iterUdis;
1055
1056 /* Connect to hal */
1057 rc = halInit (&dbusConnection);
1058 if (!dbusConnection)
1059 halSuccess = false;
1060 /* Get an array of all devices in the usb_device subsystem */
1061 if (halSuccess && RT_SUCCESS (rc))
1062 {
1063 rc = halFindDeviceStringMatch (dbusConnection.get(), "info.category",
1064 "usbraw", &replyFind);
1065 if (!replyFind)
1066 halSuccess = false;
1067 }
1068 if (halSuccess && RT_SUCCESS (rc))
1069 {
1070 dbus_message_iter_init (replyFind.get(), &iterFind);
1071 if (dbus_message_iter_get_arg_type (&iterFind) != DBUS_TYPE_ARRAY)
1072 halSuccess = false;
1073 }
1074 /* Recurse down into the array and query interesting information about the
1075 * entries. */
1076 if (halSuccess && RT_SUCCESS (rc))
1077 dbus_message_iter_recurse (&iterFind, &iterUdis);
1078 for (; halSuccess && RT_SUCCESS (rc)
1079 && dbus_message_iter_get_arg_type (&iterUdis) == DBUS_TYPE_STRING;
1080 dbus_message_iter_next(&iterUdis))
1081 {
1082 /* Get the device node and the sysfs path for the current entry. */
1083 const char *pszUdi;
1084 dbus_message_iter_get_basic (&iterUdis, &pszUdi);
1085 static const char *papszKeys[] = { "linux.device_file", "info.parent" };
1086 char *papszValues[RT_ELEMENTS (papszKeys)];
1087 rc = halGetPropertyStrings (dbusConnection.get(), pszUdi, RT_ELEMENTS (papszKeys),
1088 papszKeys, papszValues, &replyGet);
1089 const char *pszDevice = papszValues[0], *pszSysfsPath = papszValues[1];
1090 /* Get the interfaces. */
1091 if (!!replyGet && pszDevice && pszSysfsPath)
1092 {
1093 USBDeviceInfo info (pszDevice, pszSysfsPath);
1094 bool ifaceSuccess = false; /* If we can't get the interfaces, just
1095 * skip this one device. */
1096 rc = getUSBInterfacesFromHal (&info.mInterfaces, pszSysfsPath,
1097 &ifaceSuccess);
1098 if (RT_SUCCESS(rc) && halSuccess && ifaceSuccess)
1099 pList->push_back (info);
1100 }
1101 }
1102 if (dbusError.HasName (DBUS_ERROR_NO_MEMORY))
1103 rc = VERR_NO_MEMORY;
1104 if (pfSuccess != NULL)
1105 *pfSuccess = halSuccess;
1106 LogFlow (("rc=%Rrc, halSuccess=%d\n", rc, halSuccess));
1107 dbusError.FlowLog();
1108 return rc;
1109}
1110
1111/**
1112 * Helper function to query the hal subsystem for information about USB devices
1113 * attached to the system.
1114 * @returns iprt status code
1115 * @param pList where to add information about the devices detected. If
1116 * certain interfaces are not found (@a pfFound is false on
1117 * return) this may contain invalid information.
1118 * @param pcszUdi the hal UDI of the device
1119 * @param pfSuccess will be set to true if the operation succeeds and to
1120 * false if it fails for non-critical reasons. Optional.
1121 *
1122 * @returns IPRT status code
1123 */
1124/* static */
1125int getUSBInterfacesFromHal(std::vector <std::string> *pList,
1126 const char *pcszUdi, bool *pfSuccess)
1127{
1128 AssertReturn(VALID_PTR (pList) && VALID_PTR (pcszUdi) &&
1129 (pfSuccess == NULL || VALID_PTR (pfSuccess)),
1130 VERR_INVALID_POINTER);
1131 LogFlowFunc (("pList=%p, pcszUdi=%s, pfSuccess=%p\n", pList, pcszUdi,
1132 pfSuccess));
1133 int rc = VINF_SUCCESS; /* We set this to failure on fatal errors. */
1134 bool halSuccess = true; /* We set this to false to abort the operation. */
1135 autoDBusError dbusError;
1136
1137 RTMemAutoPtr <DBusMessage, VBoxDBusMessageUnref> message, replyFind, replyGet;
1138 RTMemAutoPtr <DBusConnection, VBoxHalShutdown> dbusConnection;
1139 DBusMessageIter iterFind, iterUdis;
1140
1141 rc = halInit (&dbusConnection);
1142 if (!dbusConnection)
1143 halSuccess = false;
1144 if (halSuccess && RT_SUCCESS (rc))
1145 {
1146 /* Look for children of the current UDI. */
1147 rc = halFindDeviceStringMatch (dbusConnection.get(), "info.parent",
1148 pcszUdi, &replyFind);
1149 if (!replyFind)
1150 halSuccess = false;
1151 }
1152 if (halSuccess && RT_SUCCESS (rc))
1153 {
1154 dbus_message_iter_init (replyFind.get(), &iterFind);
1155 if (dbus_message_iter_get_arg_type (&iterFind) != DBUS_TYPE_ARRAY)
1156 halSuccess = false;
1157 }
1158 if (halSuccess && RT_SUCCESS (rc))
1159 dbus_message_iter_recurse (&iterFind, &iterUdis);
1160 for (; halSuccess && RT_SUCCESS (rc)
1161 && dbus_message_iter_get_arg_type (&iterUdis) == DBUS_TYPE_STRING;
1162 dbus_message_iter_next(&iterUdis))
1163 {
1164 /* Now get the sysfs path and the subsystem from the iterator */
1165 const char *pszUdi;
1166 dbus_message_iter_get_basic (&iterUdis, &pszUdi);
1167 static const char *papszKeys[] = { "linux.sysfs_path", "info.subsystem",
1168 "linux.subsystem" };
1169 char *papszValues[RT_ELEMENTS (papszKeys)];
1170 rc = halGetPropertyStrings (dbusConnection.get(), pszUdi, RT_ELEMENTS (papszKeys),
1171 papszKeys, papszValues, &replyGet);
1172 const char *pszSysfsPath = papszValues[0], *pszInfoSubsystem = papszValues[1],
1173 *pszLinuxSubsystem = papszValues[2];
1174 if (!replyGet)
1175 halSuccess = false;
1176 if (!!replyGet && pszSysfsPath == NULL)
1177 halSuccess = false;
1178 if ( halSuccess && RT_SUCCESS (rc)
1179 && RTStrCmp (pszInfoSubsystem, "usb_device") != 0 /* Children of buses can also be devices. */
1180 && RTStrCmp (pszLinuxSubsystem, "usb_device") != 0)
1181 pList->push_back (pszSysfsPath);
1182 }
1183 if (dbusError.HasName (DBUS_ERROR_NO_MEMORY))
1184 rc = VERR_NO_MEMORY;
1185 if (pfSuccess != NULL)
1186 *pfSuccess = halSuccess;
1187 LogFlow (("rc=%Rrc, halSuccess=%d\n", rc, halSuccess));
1188 dbusError.FlowLog();
1189 return rc;
1190}
1191
1192/**
1193 * When it is registered with DBus, this function will be called by
1194 * dbus_connection_read_write_dispatch each time a message is received over the
1195 * DBus connection. We check whether that message was caused by a hal device
1196 * hotplug event, and if so we set a flag. dbus_connection_read_write_dispatch
1197 * will return after calling its filter functions, and its caller should then
1198 * check the status of the flag passed to the filter function.
1199 *
1200 * @param pConnection The DBus connection we are using.
1201 * @param pMessage The DBus message which just arrived.
1202 * @param pvUser A pointer to the flag variable we are to set.
1203 */
1204/* static */
1205DBusHandlerResult dbusFilterFunction (DBusConnection *pConnection,
1206 DBusMessage *pMessage, void *pvUser)
1207{
1208 bool *pTriggered = reinterpret_cast<bool *> (pvUser);
1209 if ( dbus_message_is_signal (pMessage, "org.freedesktop.Hal.Manager",
1210 "DeviceAdded")
1211 || dbus_message_is_signal (pMessage, "org.freedesktop.Hal.Manager",
1212 "DeviceRemoved"))
1213 {
1214 *pTriggered = true;
1215 }
1216 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1217}
1218#endif /* RT_OS_LINUX && VBOX_WITH_DBUS */
1219
Note: See TracBrowser for help on using the repository browser.

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