VirtualBox

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

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

Main/HostHardwareLinux: assertions

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 55.2 KB
Line 
1/* $Id: HostHardwareLinux.cpp 16543 2009-02-06 11:16:03Z vboxsync $ */
2/** @file
3 * Classes for handling hardware detection under Linux. Please feel free to
4 * expand these to work for other systems (Solaris!) or to add new ones for
5 * other systems.
6 */
7
8/*
9 * Copyright (C) 2008 Sun Microsystems, Inc.
10 *
11 * This file is part of VirtualBox Open Source Edition (OSE), as
12 * available from http://www.virtualbox.org. This file is free software;
13 * you can redistribute it and/or modify it under the terms of the GNU
14 * General Public License (GPL) as published by the Free Software
15 * Foundation, in version 2 as it comes in the "COPYING" file of the
16 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
17 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
18 *
19 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
20 * Clara, CA 95054 USA or visit http://www.sun.com if you need
21 * additional information or have any questions.
22 */
23
24#define LOG_GROUP LOG_GROUP_MAIN
25
26/*******************************************************************************
27* Header Files *
28*******************************************************************************/
29
30#include <HostHardwareLinux.h>
31
32#include <VBox/log.h>
33
34#include <iprt/env.h>
35#include <iprt/mem.h>
36#include <iprt/string.h>
37
38#ifdef RT_OS_LINUX
39# include <sys/types.h>
40# include <sys/stat.h>
41# include <unistd.h>
42# include <sys/ioctl.h>
43# include <fcntl.h>
44# include <mntent.h>
45/* bird: This is a hack to work around conflicts between these linux kernel headers
46 * and the GLIBC tcpip headers. They have different declarations of the 4
47 * standard byte order functions. */
48// # define _LINUX_BYTEORDER_GENERIC_H
49# define _LINUX_BYTEORDER_SWABB_H
50# include <linux/cdrom.h>
51# ifdef VBOX_WITH_DBUS
52# include <vbox-dbus.h>
53# endif
54# include <errno.h>
55#endif /* RT_OS_LINUX */
56#include <string>
57#include <vector>
58
59/*******************************************************************************
60* Global Variables *
61*******************************************************************************/
62
63bool g_testHostHardwareLinux = false;
64static bool testing () { return g_testHostHardwareLinux; }
65
66/*******************************************************************************
67* Typedefs and Defines *
68*******************************************************************************/
69
70/** When waiting for hotplug events, we currently restart the wait after at
71 * most this many milliseconds. */
72enum { DBUS_POLL_TIMEOUT = 2000 /* ms */ };
73
74
75static bool validateDevice(const char *deviceNode, bool isDVD);
76static int getDriveInfoFromEnv(const char *pszVar, DriveInfoList *pList,
77 bool isDVD, bool *pfSuccess);
78static int getDVDInfoFromMTab(char *mountTable, DriveInfoList *pList);
79#ifdef VBOX_WITH_DBUS
80/* These must be extern to be usable in the RTMemAutoPtr template */
81extern void VBoxHalShutdown (DBusConnection *pConnection);
82extern void VBoxHalShutdownPrivate (DBusConnection *pConnection);
83extern void VBoxDBusConnectionUnref(DBusConnection *pConnection);
84extern void VBoxDBusConnectionCloseAndUnref(DBusConnection *pConnection);
85extern void VBoxDBusMessageUnref(DBusMessage *pMessage);
86
87static int halInit(RTMemAutoPtr <DBusConnection, VBoxHalShutdown> *pConnection);
88static int halInitPrivate(RTMemAutoPtr <DBusConnection, VBoxHalShutdownPrivate> *pConnection);
89static int halFindDeviceStringMatch (DBusConnection *pConnection,
90 const char *pszKey, const char *pszValue,
91 RTMemAutoPtr <DBusMessage, VBoxDBusMessageUnref> *pMessage);
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 AssertPtrReturn (pConnection, VERR_INVALID_POINTER);
842 AssertPtrReturn (pszKey, VERR_INVALID_POINTER);
843 AssertPtrReturn (pszValue, VERR_INVALID_POINTER);
844 AssertPtrReturn (pMatches, VERR_INVALID_POINTER);
845 AssertReturn (pfSuccess == NULL || VALID_PTR (pfSuccess), VERR_INVALID_POINTER);
846 LogFlowFunc (("pConnection=%p, pszKey=%s, pszValue=%s, pMatches=%p, pfSuccess=%p\n",
847 pConnection, pszKey, pszValue, pMatches, pfSuccess));
848 int rc = VINF_SUCCESS; /* We set this to failure on fatal errors. */
849 bool halSuccess = true; /* We set this to false to abort the operation. */
850
851 RTMemAutoPtr <DBusMessage, VBoxDBusMessageUnref> message, replyFind;
852 DBusMessageIter iterFind, iterUdis;
853
854 if (halSuccess && RT_SUCCESS (rc))
855 {
856 rc = halFindDeviceStringMatch (pConnection, pszKey, pszValue,
857 &replyFind);
858 if (!replyFind)
859 halSuccess = false;
860 }
861 if (halSuccess && RT_SUCCESS (rc))
862 {
863 dbus_message_iter_init (replyFind.get(), &iterFind);
864 if (dbus_message_iter_get_arg_type (&iterFind) != DBUS_TYPE_ARRAY)
865 halSuccess = false;
866 }
867 if (halSuccess && RT_SUCCESS (rc))
868 dbus_message_iter_recurse (&iterFind, &iterUdis);
869 for (; halSuccess && RT_SUCCESS (rc)
870 && dbus_message_iter_get_arg_type (&iterUdis) == DBUS_TYPE_STRING;
871 dbus_message_iter_next(&iterUdis))
872 {
873 /* Now get all UDIs from the iterator */
874 const char *pszUdi;
875 dbus_message_iter_get_basic (&iterUdis, &pszUdi);
876 try
877 {
878 pMatches->push_back(pszUdi);
879 }
880 catch (std::bad_alloc)
881 {
882 rc = VERR_NO_MEMORY;
883 }
884 }
885 if (pfSuccess != NULL)
886 *pfSuccess = halSuccess;
887 LogFlow (("rc=%Rrc, halSuccess=%d\n", rc, halSuccess));
888 return rc;
889}
890
891/**
892 * Read a set of string properties for a device. If some of the properties are
893 * not of type DBUS_TYPE_STRING or do not exist then a NULL pointer will be
894 * returned for them.
895 * @returns iprt status code. If the operation failed for non-fatal reasons
896 * then we return success and leave pMessage untouched - reset it
897 * before the call to detect this.
898 * @param pConnection an initialised connection DBus
899 * @param pszUdi the Udi of the device
900 * @param cProps the number of property values to look up
901 * @param papszKeys the keys of the properties to be looked up
902 * @param papszValues where to store the values of the properties. The
903 * strings returned will be valid until the message
904 * returned in @a ppMessage is freed. Undefined if
905 * the message is NULL.
906 * @param pMessage where to store the return DBus message. The caller
907 * is responsible for freeing this once they have
908 * finished with the value strings. NOT optional.
909 */
910/* static */
911int halGetPropertyStrings (DBusConnection *pConnection, const char *pszUdi,
912 size_t cProps, const char **papszKeys,
913 char **papszValues,
914 RTMemAutoPtr <DBusMessage, VBoxDBusMessageUnref> *pMessage)
915{
916 AssertReturn( VALID_PTR (pConnection) && VALID_PTR (pszUdi)
917 && VALID_PTR (papszKeys) && VALID_PTR (papszValues)
918 && VALID_PTR (pMessage),
919 VERR_INVALID_POINTER);
920 LogFlowFunc (("pConnection=%p, pszUdi=%s, cProps=%llu, papszKeys=%p, papszValues=%p, pMessage=%p\n",
921 pConnection, pszUdi, cProps, papszKeys, papszValues, pMessage));
922 int rc = VINF_SUCCESS; /* We set this to failure on fatal errors. */
923 bool halSuccess = true; /* We set this to false to abort the operation. */
924 autoDBusError dbusError;
925
926 RTMemAutoPtr <DBusMessage, VBoxDBusMessageUnref> message, reply;
927 DBusMessageIter iterGet, iterProps, iterKey, iterValue;
928
929 /* Initialise the return array to NULLs */
930 for (size_t i = 0; i < cProps; ++i)
931 papszValues[i] = NULL;
932
933 /* Send a GetAllProperties message to hald */
934 message = dbus_message_new_method_call ("org.freedesktop.Hal", pszUdi,
935 "org.freedesktop.Hal.Device",
936 "GetAllProperties");
937 if (!message)
938 rc = VERR_NO_MEMORY;
939 if (halSuccess && RT_SUCCESS (rc))
940 {
941 reply = dbus_connection_send_with_reply_and_block (pConnection,
942 message.get(), -1,
943 &dbusError.get());
944 if (!reply)
945 halSuccess = false;
946 }
947
948 /* Parse the reply */
949 if (halSuccess && RT_SUCCESS (rc))
950 {
951 dbus_message_iter_init (reply.get(), &iterGet);
952 if ( dbus_message_iter_get_arg_type (&iterGet) != DBUS_TYPE_ARRAY
953 && dbus_message_iter_get_element_type (&iterGet) != DBUS_TYPE_DICT_ENTRY)
954 halSuccess = false;
955 }
956 if (halSuccess && RT_SUCCESS (rc))
957 dbus_message_iter_recurse (&iterGet, &iterProps);
958 /* Go through all entries in the reply and see if any match our keys. */
959 while ( halSuccess && RT_SUCCESS (rc)
960 && dbus_message_iter_get_arg_type (&iterProps)
961 == DBUS_TYPE_DICT_ENTRY)
962 {
963 const char *pszKey;
964 DBusMessageIter iterEntry, iterValue;
965 dbus_message_iter_recurse (&iterProps, &iterEntry);
966 dbus_message_iter_get_basic (&iterEntry, &pszKey);
967 dbus_message_iter_next (&iterEntry);
968 dbus_message_iter_recurse (&iterEntry, &iterValue);
969 /* Fill in any matches. */
970 for (size_t i = 0; i < cProps; ++i)
971 if (strcmp (pszKey, papszKeys[i]) == 0)
972 {
973 if (dbus_message_iter_get_arg_type (&iterValue) == DBUS_TYPE_STRING)
974 dbus_message_iter_get_basic (&iterValue, &papszValues[i]);
975 }
976 dbus_message_iter_next (&iterProps);
977 }
978 if (RT_SUCCESS (rc) && halSuccess)
979 *pMessage = reply.release();
980 if (dbusError.HasName (DBUS_ERROR_NO_MEMORY))
981 rc = VERR_NO_MEMORY;
982 LogFlowFunc (("rc=%Rrc, *pMessage.value()=%p\n", rc, (*pMessage).get()));
983 dbusError.FlowLog();
984 return rc;
985}
986
987/**
988 * Read a set of string properties for a device. If some properties do not
989 * exist or are not of type DBUS_TYPE_STRING, we will still fetch the others.
990 * @returns iprt status code. If the operation failed for non-fatal reasons
991 * then we return success and set *pfSuccess to false.
992 * @param pConnection an initialised connection DBus
993 * @param pszUdi the Udi of the device
994 * @param cProps the number of property values to look up
995 * @param papszKeys the keys of the properties to be looked up
996 * @param pMatches pointer to an empty array of std::string to append the
997 * results to. NOT optional.
998 * @param pfMatches pointer to an array of boolean values indicating
999 * whether the respective property is a string. If this
1000 * is not supplied then all properties must be strings
1001 * for the operation to be considered successful
1002 * @param pfSuccess will be set to true if the operation succeeds
1003 */
1004/* static */
1005int halGetPropertyStringsVector (DBusConnection *pConnection,
1006 const char *pszUdi, size_t cProps,
1007 const char **papszKeys,
1008 std::vector<std::string> *pMatches,
1009 bool *pfMatches, bool *pfSuccess)
1010{
1011 AssertPtrReturn (pConnection, VERR_INVALID_POINTER);
1012 AssertPtrReturn (pszUdi, VERR_INVALID_POINTER);
1013 AssertPtrReturn (papszKeys, VERR_INVALID_POINTER);
1014 AssertPtrReturn (pMatches, VERR_INVALID_POINTER);
1015 AssertReturn ((pfMatches == NULL) || VALID_PTR (pfMatches), VERR_INVALID_POINTER);
1016 AssertReturn ((pfSuccess == NULL) || VALID_PTR (pfSuccess), VERR_INVALID_POINTER);
1017 AssertReturn(pMatches->empty(), VERR_INVALID_PARAMETER);
1018 LogFlowFunc (("pConnection=%p, pszUdi=%s, cProps=%llu, papszKeys=%p, pMatches=%p, pfMatches=%p, pfSuccess=%p\n",
1019 pConnection, pszUdi, cProps, papszKeys, pMatches, pfMatches, pfSuccess));
1020 RTMemAutoPtr <char *> values(cProps);
1021 RTMemAutoPtr <DBusMessage, VBoxDBusMessageUnref> message;
1022 bool halSuccess = true;
1023 int rc = halGetPropertyStrings (pConnection, pszUdi, cProps, papszKeys,
1024 values.get(), &message);
1025 if (!message)
1026 halSuccess = false;
1027 for (size_t i = 0; RT_SUCCESS(rc) && halSuccess && i < cProps; ++i)
1028 {
1029 bool fMatches = values[i] != NULL;
1030 if (pfMatches != NULL)
1031 pfMatches[i] = fMatches;
1032 else
1033 halSuccess = fMatches;
1034 try
1035 {
1036 pMatches->push_back(fMatches ? values[i] : "");
1037 }
1038 catch (std::bad_alloc)
1039 {
1040 rc = VERR_NO_MEMORY;
1041 }
1042 }
1043 if (pfSuccess != NULL)
1044 *pfSuccess = halSuccess;
1045 if (RT_SUCCESS(rc) && halSuccess)
1046 {
1047 Assert (pMatches->size() == cProps);
1048 AssertForEach (j, size_t, 0, cProps, (pfMatches == NULL)
1049 || (pfMatches[j] == true)
1050 || ((pfMatches[j] == false) && (pMatches[j].size() == 0)));
1051 }
1052 LogFlowFunc (("rc=%Rrc, halSuccess=%d\n", rc, halSuccess));
1053 return rc;
1054}
1055
1056/**
1057 * Helper function to query the hal subsystem for information about drives
1058 * attached to the system.
1059 * @returns iprt status code
1060 * @param pList where to add information about the drives detected
1061 * @param isDVD are we looking for DVDs or floppies?
1062 * @param pfSuccess will be set to true if all interactions with hal
1063 * succeeded and to false otherwise. Optional.
1064 *
1065 * @returns IPRT status code
1066 */
1067/* static */
1068int getDriveInfoFromHal(DriveInfoList *pList, bool isDVD, bool *pfSuccess)
1069{
1070 AssertReturn(VALID_PTR (pList) && (pfSuccess == NULL || VALID_PTR (pfSuccess)),
1071 VERR_INVALID_POINTER);
1072 LogFlowFunc (("pList=%p, isDVD=%d, pfSuccess=%p\n", pList, isDVD, pfSuccess));
1073 int rc = VINF_SUCCESS; /* We set this to failure on fatal errors. */
1074 bool halSuccess = true; /* We set this to false to abort the operation. */
1075 autoDBusError dbusError;
1076
1077 RTMemAutoPtr <DBusMessage, VBoxDBusMessageUnref> message, replyFind, replyGet;
1078 RTMemAutoPtr <DBusConnection, VBoxHalShutdown> dbusConnection;
1079 DBusMessageIter iterFind, iterUdis;
1080
1081 rc = halInit (&dbusConnection);
1082 if (!dbusConnection)
1083 halSuccess = false;
1084 if (halSuccess && RT_SUCCESS (rc))
1085 {
1086 rc = halFindDeviceStringMatch (dbusConnection.get(), "storage.drive_type",
1087 isDVD ? "cdrom" : "floppy", &replyFind);
1088 if (!replyFind)
1089 halSuccess = false;
1090 }
1091 if (halSuccess && RT_SUCCESS (rc))
1092 {
1093 dbus_message_iter_init (replyFind.get(), &iterFind);
1094 if (dbus_message_iter_get_arg_type (&iterFind) != DBUS_TYPE_ARRAY)
1095 halSuccess = false;
1096 }
1097 if (halSuccess && RT_SUCCESS (rc))
1098 dbus_message_iter_recurse (&iterFind, &iterUdis);
1099 for (; halSuccess && RT_SUCCESS (rc)
1100 && dbus_message_iter_get_arg_type (&iterUdis) == DBUS_TYPE_STRING;
1101 dbus_message_iter_next(&iterUdis))
1102 {
1103 /* Now get all properties from the iterator */
1104 const char *pszUdi;
1105 dbus_message_iter_get_basic (&iterUdis, &pszUdi);
1106 static const char *papszKeys[] =
1107 { "block.device", "info.product", "info.vendor" };
1108 char *papszValues[RT_ELEMENTS (papszKeys)];
1109 rc = halGetPropertyStrings (dbusConnection.get(), pszUdi, RT_ELEMENTS (papszKeys),
1110 papszKeys, papszValues, &replyGet);
1111 std::string description;
1112 const char *pszDevice = papszValues[0], *pszProduct = papszValues[1],
1113 *pszVendor = papszValues[2];
1114 if (!!replyGet && pszDevice == NULL)
1115 halSuccess = false;
1116 if (!!replyGet && pszDevice != NULL)
1117 {
1118 if ((pszVendor != NULL) && (pszVendor[0] != '\0'))
1119 (description += pszVendor) += " ";
1120 if ((pszProduct != NULL && pszProduct[0] != '\0'))
1121 description += pszProduct;
1122 try
1123 {
1124 pList->push_back (DriveInfo (pszDevice, pszUdi, description));
1125 }
1126 catch (std::bad_alloc)
1127 {
1128 rc = VERR_NO_MEMORY;
1129 }
1130 }
1131 }
1132 if (dbusError.HasName (DBUS_ERROR_NO_MEMORY))
1133 rc = VERR_NO_MEMORY;
1134 if (pfSuccess != NULL)
1135 *pfSuccess = halSuccess;
1136 LogFlow (("rc=%Rrc, halSuccess=%d\n", rc, halSuccess));
1137 dbusError.FlowLog();
1138 return rc;
1139}
1140
1141/**
1142 * Helper function to query the hal subsystem for information about USB devices
1143 * attached to the system.
1144 * @returns iprt status code
1145 * @param pList where to add information about the devices detected
1146 * @param pfSuccess will be set to true if all interactions with hal
1147 * succeeded and to false otherwise. Optional.
1148 *
1149 * @returns IPRT status code
1150 */
1151/* static */
1152int getUSBDeviceInfoFromHal(USBDeviceInfoList *pList, bool *pfSuccess)
1153{
1154 AssertReturn(VALID_PTR (pList) && (pfSuccess == NULL || VALID_PTR (pfSuccess)),
1155 VERR_INVALID_POINTER);
1156 LogFlowFunc (("pList=%p, pfSuccess=%p\n", pList, pfSuccess));
1157 int rc = VINF_SUCCESS; /* We set this to failure on fatal errors. */
1158 bool halSuccess = true; /* We set this to false to abort the operation. */
1159 autoDBusError dbusError;
1160
1161 RTMemAutoPtr <DBusMessage, VBoxDBusMessageUnref> message, replyFind, replyGet;
1162 RTMemAutoPtr <DBusConnection, VBoxHalShutdown> dbusConnection;
1163 DBusMessageIter iterFind, iterUdis;
1164
1165 /* Connect to hal */
1166 rc = halInit (&dbusConnection);
1167 if (!dbusConnection)
1168 halSuccess = false;
1169 /* Get an array of all devices in the usb_device subsystem */
1170 if (halSuccess && RT_SUCCESS (rc))
1171 {
1172 rc = halFindDeviceStringMatch (dbusConnection.get(), "info.subsystem",
1173 "usb_device", &replyFind);
1174 if (!replyFind)
1175 halSuccess = false;
1176 }
1177 if (halSuccess && RT_SUCCESS (rc))
1178 {
1179 dbus_message_iter_init (replyFind.get(), &iterFind);
1180 if (dbus_message_iter_get_arg_type (&iterFind) != DBUS_TYPE_ARRAY)
1181 halSuccess = false;
1182 }
1183 /* Recurse down into the array and query interesting information about the
1184 * entries. */
1185 if (halSuccess && RT_SUCCESS (rc))
1186 dbus_message_iter_recurse (&iterFind, &iterUdis);
1187 for (; halSuccess && RT_SUCCESS (rc)
1188 && dbus_message_iter_get_arg_type (&iterUdis) == DBUS_TYPE_STRING;
1189 dbus_message_iter_next(&iterUdis))
1190 {
1191 /* Get the device node and the sysfs path for the current entry. */
1192 const char *pszUdi;
1193 dbus_message_iter_get_basic (&iterUdis, &pszUdi);
1194 static const char *papszKeys[] = { "linux.device_file", "linux.sysfs_path" };
1195 char *papszValues[RT_ELEMENTS (papszKeys)];
1196 rc = halGetPropertyStrings (dbusConnection.get(), pszUdi, RT_ELEMENTS (papszKeys),
1197 papszKeys, papszValues, &replyGet);
1198 const char *pszDevice = papszValues[0], *pszSysfsPath = papszValues[1];
1199 /* Get the interfaces. */
1200 if (!!replyGet && pszDevice && pszSysfsPath)
1201 {
1202 USBDeviceInfo info (pszDevice, pszSysfsPath);
1203 bool ifaceSuccess = true; /* If we can't get the interfaces, just
1204 * skip this one device. */
1205 rc = getUSBInterfacesFromHal (&info.mInterfaces, pszUdi, &ifaceSuccess);
1206 if (RT_SUCCESS(rc) && halSuccess && ifaceSuccess)
1207 try
1208 {
1209 pList->push_back (info);
1210 }
1211 catch (std::bad_alloc)
1212 {
1213 rc = VERR_NO_MEMORY;
1214 }
1215 }
1216 }
1217 if (dbusError.HasName (DBUS_ERROR_NO_MEMORY))
1218 rc = VERR_NO_MEMORY;
1219 if (pfSuccess != NULL)
1220 *pfSuccess = halSuccess;
1221 LogFlow (("rc=%Rrc, halSuccess=%d\n", rc, halSuccess));
1222 dbusError.FlowLog();
1223 return rc;
1224}
1225
1226/**
1227 * Helper function to query the hal subsystem for information about USB devices
1228 * attached to the system, using the older API.
1229 * @returns iprt status code
1230 * @param pList where to add information about the devices detected
1231 * @param pfSuccess will be set to true if all interactions with hal
1232 * succeeded and to false otherwise. Optional.
1233 *
1234 * @returns IPRT status code
1235 */
1236/* static */
1237int getOldUSBDeviceInfoFromHal(USBDeviceInfoList *pList, bool *pfSuccess)
1238{
1239 AssertReturn(VALID_PTR (pList) && (pfSuccess == NULL || VALID_PTR (pfSuccess)),
1240 VERR_INVALID_POINTER);
1241 LogFlowFunc (("pList=%p, pfSuccess=%p\n", pList, pfSuccess));
1242 int rc = VINF_SUCCESS; /* We set this to failure on fatal errors. */
1243 bool halSuccess = true; /* We set this to false to abort the operation. */
1244 autoDBusError dbusError;
1245
1246 RTMemAutoPtr <DBusMessage, VBoxDBusMessageUnref> message, replyFind, replyGet;
1247 RTMemAutoPtr <DBusConnection, VBoxHalShutdown> dbusConnection;
1248 DBusMessageIter iterFind, iterUdis;
1249
1250 /* Connect to hal */
1251 rc = halInit (&dbusConnection);
1252 if (!dbusConnection)
1253 halSuccess = false;
1254 /* Get an array of all devices in the usb_device subsystem */
1255 if (halSuccess && RT_SUCCESS (rc))
1256 {
1257 rc = halFindDeviceStringMatch (dbusConnection.get(), "info.category",
1258 "usbraw", &replyFind);
1259 if (!replyFind)
1260 halSuccess = false;
1261 }
1262 if (halSuccess && RT_SUCCESS (rc))
1263 {
1264 dbus_message_iter_init (replyFind.get(), &iterFind);
1265 if (dbus_message_iter_get_arg_type (&iterFind) != DBUS_TYPE_ARRAY)
1266 halSuccess = false;
1267 }
1268 /* Recurse down into the array and query interesting information about the
1269 * entries. */
1270 if (halSuccess && RT_SUCCESS (rc))
1271 dbus_message_iter_recurse (&iterFind, &iterUdis);
1272 for (; halSuccess && RT_SUCCESS (rc)
1273 && dbus_message_iter_get_arg_type (&iterUdis) == DBUS_TYPE_STRING;
1274 dbus_message_iter_next(&iterUdis))
1275 {
1276 /* Get the device node and the sysfs path for the current entry. */
1277 const char *pszUdi;
1278 dbus_message_iter_get_basic (&iterUdis, &pszUdi);
1279 static const char *papszKeys[] = { "linux.device_file", "info.parent" };
1280 char *papszValues[RT_ELEMENTS (papszKeys)];
1281 rc = halGetPropertyStrings (dbusConnection.get(), pszUdi, RT_ELEMENTS (papszKeys),
1282 papszKeys, papszValues, &replyGet);
1283 const char *pszDevice = papszValues[0], *pszSysfsPath = papszValues[1];
1284 /* Get the interfaces. */
1285 if (!!replyGet && pszDevice && pszSysfsPath)
1286 {
1287 USBDeviceInfo info (pszDevice, pszSysfsPath);
1288 bool ifaceSuccess = false; /* If we can't get the interfaces, just
1289 * skip this one device. */
1290 rc = getUSBInterfacesFromHal (&info.mInterfaces, pszSysfsPath,
1291 &ifaceSuccess);
1292 if (RT_SUCCESS(rc) && halSuccess && ifaceSuccess)
1293 try
1294 {
1295 pList->push_back (info);
1296 }
1297 catch (std::bad_alloc)
1298 {
1299 rc = VERR_NO_MEMORY;
1300 }
1301 }
1302 }
1303 if (dbusError.HasName (DBUS_ERROR_NO_MEMORY))
1304 rc = VERR_NO_MEMORY;
1305 if (pfSuccess != NULL)
1306 *pfSuccess = halSuccess;
1307 LogFlow (("rc=%Rrc, halSuccess=%d\n", rc, halSuccess));
1308 dbusError.FlowLog();
1309 return rc;
1310}
1311
1312/**
1313 * Helper function to query the hal subsystem for information about USB devices
1314 * attached to the system.
1315 * @returns iprt status code
1316 * @param pList where to add information about the devices detected. If
1317 * certain interfaces are not found (@a pfFound is false on
1318 * return) this may contain invalid information.
1319 * @param pcszUdi the hal UDI of the device
1320 * @param pfSuccess will be set to true if the operation succeeds and to
1321 * false if it fails for non-critical reasons. Optional.
1322 *
1323 * @returns IPRT status code
1324 */
1325/* static */
1326int getUSBInterfacesFromHal(std::vector <std::string> *pList,
1327 const char *pcszUdi, bool *pfSuccess)
1328{
1329 AssertReturn(VALID_PTR (pList) && VALID_PTR (pcszUdi) &&
1330 (pfSuccess == NULL || VALID_PTR (pfSuccess)),
1331 VERR_INVALID_POINTER);
1332 LogFlowFunc (("pList=%p, pcszUdi=%s, pfSuccess=%p\n", pList, pcszUdi,
1333 pfSuccess));
1334 int rc = VINF_SUCCESS; /* We set this to failure on fatal errors. */
1335 bool halSuccess = true; /* We set this to false to abort the operation. */
1336 autoDBusError dbusError;
1337
1338 RTMemAutoPtr <DBusMessage, VBoxDBusMessageUnref> message, replyFind, replyGet;
1339 RTMemAutoPtr <DBusConnection, VBoxHalShutdown> dbusConnection;
1340 DBusMessageIter iterFind, iterUdis;
1341
1342 rc = halInit (&dbusConnection);
1343 if (!dbusConnection)
1344 halSuccess = false;
1345 if (halSuccess && RT_SUCCESS (rc))
1346 {
1347 /* Look for children of the current UDI. */
1348 rc = halFindDeviceStringMatch (dbusConnection.get(), "info.parent",
1349 pcszUdi, &replyFind);
1350 if (!replyFind)
1351 halSuccess = false;
1352 }
1353 if (halSuccess && RT_SUCCESS (rc))
1354 {
1355 dbus_message_iter_init (replyFind.get(), &iterFind);
1356 if (dbus_message_iter_get_arg_type (&iterFind) != DBUS_TYPE_ARRAY)
1357 halSuccess = false;
1358 }
1359 if (halSuccess && RT_SUCCESS (rc))
1360 dbus_message_iter_recurse (&iterFind, &iterUdis);
1361 for (; halSuccess && RT_SUCCESS (rc)
1362 && dbus_message_iter_get_arg_type (&iterUdis) == DBUS_TYPE_STRING;
1363 dbus_message_iter_next(&iterUdis))
1364 {
1365 /* Now get the sysfs path and the subsystem from the iterator */
1366 const char *pszUdi;
1367 dbus_message_iter_get_basic (&iterUdis, &pszUdi);
1368 static const char *papszKeys[] = { "linux.sysfs_path", "info.subsystem",
1369 "linux.subsystem" };
1370 char *papszValues[RT_ELEMENTS (papszKeys)];
1371 rc = halGetPropertyStrings (dbusConnection.get(), pszUdi, RT_ELEMENTS (papszKeys),
1372 papszKeys, papszValues, &replyGet);
1373 const char *pszSysfsPath = papszValues[0], *pszInfoSubsystem = papszValues[1],
1374 *pszLinuxSubsystem = papszValues[2];
1375 if (!replyGet)
1376 halSuccess = false;
1377 if (!!replyGet && pszSysfsPath == NULL)
1378 halSuccess = false;
1379 if ( halSuccess && RT_SUCCESS (rc)
1380 && RTStrCmp (pszInfoSubsystem, "usb_device") != 0 /* Children of buses can also be devices. */
1381 && RTStrCmp (pszLinuxSubsystem, "usb_device") != 0)
1382 try
1383 {
1384 pList->push_back (pszSysfsPath);
1385 }
1386 catch (std::bad_alloc)
1387 {
1388 rc = VERR_NO_MEMORY;
1389 }
1390 }
1391 if (dbusError.HasName (DBUS_ERROR_NO_MEMORY))
1392 rc = VERR_NO_MEMORY;
1393 if (pfSuccess != NULL)
1394 *pfSuccess = halSuccess;
1395 LogFlow (("rc=%Rrc, halSuccess=%d\n", rc, halSuccess));
1396 dbusError.FlowLog();
1397 return rc;
1398}
1399
1400/**
1401 * When it is registered with DBus, this function will be called by
1402 * dbus_connection_read_write_dispatch each time a message is received over the
1403 * DBus connection. We check whether that message was caused by a hal device
1404 * hotplug event, and if so we set a flag. dbus_connection_read_write_dispatch
1405 * will return after calling its filter functions, and its caller should then
1406 * check the status of the flag passed to the filter function.
1407 *
1408 * @param pConnection The DBus connection we are using.
1409 * @param pMessage The DBus message which just arrived.
1410 * @param pvUser A pointer to the flag variable we are to set.
1411 */
1412/* static */
1413DBusHandlerResult dbusFilterFunction (DBusConnection *pConnection,
1414 DBusMessage *pMessage, void *pvUser)
1415{
1416 volatile bool *pTriggered = reinterpret_cast<volatile bool *> (pvUser);
1417 if ( dbus_message_is_signal (pMessage, "org.freedesktop.Hal.Manager",
1418 "DeviceAdded")
1419 || dbus_message_is_signal (pMessage, "org.freedesktop.Hal.Manager",
1420 "DeviceRemoved"))
1421 {
1422 *pTriggered = true;
1423 }
1424 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1425}
1426#endif /* RT_OS_LINUX && VBOX_WITH_DBUS */
1427
Note: See TracBrowser for help on using the repository browser.

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