VirtualBox

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

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

Main/HostHardwareLinux: fixed some uncaught exceptions and added two unused wrappers for querying hal more nicely

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