VirtualBox

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

Last change on this file since 22388 was 21878, checked in by vboxsync, 15 years ago

Main: coding style: have Main obey the standard VirtualBox coding style rules (no functional changes)

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