VirtualBox

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

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

Main: fix some exception handling

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 55.1 KB
Line 
1/* $Id: HostHardwareLinux.cpp 21432 2009-07-09 11:34:26Z 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);
92/*
93static int halFindDeviceStringMatchVector (DBusConnection *pConnection,
94 const char *pszKey,
95 const char *pszValue,
96 std::vector<std::string> *pMatches);
97*/
98static int halGetPropertyStrings (DBusConnection *pConnection,
99 const char *pszUdi, size_t cKeys,
100 const char **papszKeys, char **papszValues,
101 RTMemAutoPtr <DBusMessage, VBoxDBusMessageUnref> *pMessage);
102/*
103static int halGetPropertyStringsVector (DBusConnection *pConnection,
104 const char *pszUdi, size_t cProps,
105 const char **papszKeys,
106 std::vector<std::string> *pMatches,
107 bool *pfMatches, bool *pfSuccess);
108*/
109static int getDriveInfoFromHal(DriveInfoList *pList, bool isDVD,
110 bool *pfSuccess);
111static int getUSBDeviceInfoFromHal(USBDeviceInfoList *pList, bool *pfSuccess);
112static int getOldUSBDeviceInfoFromHal(USBDeviceInfoList *pList, bool *pfSuccess);
113static int getUSBInterfacesFromHal(std::vector <std::string> *pList,
114 const char *pcszUdi, bool *pfSuccess);
115static DBusHandlerResult dbusFilterFunction (DBusConnection *pConnection,
116 DBusMessage *pMessage, void *pvUser);
117#endif /* VBOX_WITH_DBUS */
118
119int VBoxMainDriveInfo::updateDVDs ()
120{
121 LogFlowThisFunc (("entered\n"));
122 int rc = VINF_SUCCESS;
123 bool success = false; /* Have we succeeded in finding anything yet? */
124 try
125 {
126 mDVDList.clear ();
127#if defined(RT_OS_LINUX)
128#ifdef VBOX_WITH_DBUS
129 if (RT_SUCCESS (rc) && RT_SUCCESS(VBoxLoadDBusLib()) && (!success || testing()))
130 rc = getDriveInfoFromHal(&mDVDList, true /* isDVD */, &success);
131#endif /* VBOX_WITH_DBUS defined */
132 // On Linux without hal, the situation is much more complex. We will take a
133 // heuristical approach and also allow the user to specify a list of host
134 // CDROMs using an environment variable.
135 // The general strategy is to try some known device names and see of they
136 // exist. At last, we'll enumerate the /etc/fstab file (luckily there's an
137 // API to parse it) for CDROM devices. Ok, let's start!
138 if (RT_SUCCESS (rc) && (!success || testing()))
139 rc = getDriveInfoFromEnv ("VBOX_CDROM", &mDVDList, true /* isDVD */,
140 &success);
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#ifdef VBOX_WITH_DBUS
174 if ( RT_SUCCESS (rc)
175 && RT_SUCCESS(VBoxLoadDBusLib())
176 && (!success || testing()))
177 rc = getDriveInfoFromHal(&mFloppyList, false /* isDVD */, &success);
178#endif /* VBOX_WITH_DBUS defined */
179 // As with the CDROMs, on Linux we have to take a multi-level approach
180 // involving parsing the mount tables. As this is not bulletproof, we'll
181 // give the user the chance to override the detection by an environment
182 // variable and skip the detection.
183 if (RT_SUCCESS (rc) && (!success || testing()))
184 rc = getDriveInfoFromEnv ("VBOX_FLOPPY", &mFloppyList, false /* isDVD */,
185 &success);
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 std::string.
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 std::string 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<std::string> *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 std::string 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<std::string> *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 rc = halInit (&dbusConnection);
1073 if (!dbusConnection)
1074 halSuccess = false;
1075 if (halSuccess && RT_SUCCESS (rc))
1076 {
1077 rc = halFindDeviceStringMatch (dbusConnection.get(), "storage.drive_type",
1078 isDVD ? "cdrom" : "floppy", &replyFind);
1079 if (!replyFind)
1080 halSuccess = false;
1081 }
1082 if (halSuccess && RT_SUCCESS (rc))
1083 {
1084 dbus_message_iter_init (replyFind.get(), &iterFind);
1085 if (dbus_message_iter_get_arg_type (&iterFind) != DBUS_TYPE_ARRAY)
1086 halSuccess = false;
1087 }
1088 if (halSuccess && RT_SUCCESS (rc))
1089 dbus_message_iter_recurse (&iterFind, &iterUdis);
1090 for (; halSuccess && RT_SUCCESS (rc)
1091 && dbus_message_iter_get_arg_type (&iterUdis) == DBUS_TYPE_STRING;
1092 dbus_message_iter_next(&iterUdis))
1093 {
1094 /* Now get all properties from the iterator */
1095 const char *pszUdi;
1096 dbus_message_iter_get_basic (&iterUdis, &pszUdi);
1097 static const char *papszKeys[] =
1098 { "block.device", "info.product", "info.vendor" };
1099 char *papszValues[RT_ELEMENTS (papszKeys)];
1100 rc = halGetPropertyStrings (dbusConnection.get(), pszUdi, RT_ELEMENTS (papszKeys),
1101 papszKeys, papszValues, &replyGet);
1102 std::string description;
1103 const char *pszDevice = papszValues[0], *pszProduct = papszValues[1],
1104 *pszVendor = papszValues[2];
1105 if (!!replyGet && pszDevice == NULL)
1106 halSuccess = false;
1107 if (!!replyGet && pszDevice != NULL)
1108 {
1109 if ((pszVendor != NULL) && (pszVendor[0] != '\0'))
1110 (description += pszVendor) += " ";
1111 if ((pszProduct != NULL && pszProduct[0] != '\0'))
1112 description += pszProduct;
1113 try
1114 {
1115 pList->push_back (DriveInfo (pszDevice, pszUdi, description));
1116 }
1117 catch(std::bad_alloc &e)
1118 {
1119 rc = VERR_NO_MEMORY;
1120 }
1121 }
1122 }
1123 if (dbusError.HasName (DBUS_ERROR_NO_MEMORY))
1124 rc = VERR_NO_MEMORY;
1125 if (pfSuccess != NULL)
1126 *pfSuccess = halSuccess;
1127 LogFlow (("rc=%Rrc, halSuccess=%d\n", rc, halSuccess));
1128 dbusError.FlowLog();
1129 return rc;
1130}
1131
1132/**
1133 * Helper function to query the hal subsystem for information about USB devices
1134 * attached to the system.
1135 * @returns iprt status code
1136 * @param pList where to add information about the devices detected
1137 * @param pfSuccess will be set to true if all interactions with hal
1138 * succeeded and to false otherwise. Optional.
1139 *
1140 * @returns IPRT status code
1141 */
1142/* static */
1143int getUSBDeviceInfoFromHal(USBDeviceInfoList *pList, bool *pfSuccess)
1144{
1145 AssertReturn(VALID_PTR (pList) && (pfSuccess == NULL || VALID_PTR (pfSuccess)),
1146 VERR_INVALID_POINTER);
1147 LogFlowFunc (("pList=%p, pfSuccess=%p\n", pList, pfSuccess));
1148 int rc = VINF_SUCCESS; /* We set this to failure on fatal errors. */
1149 bool halSuccess = true; /* We set this to false to abort the operation. */
1150 autoDBusError dbusError;
1151
1152 RTMemAutoPtr <DBusMessage, VBoxDBusMessageUnref> message, replyFind, replyGet;
1153 RTMemAutoPtr <DBusConnection, VBoxHalShutdown> dbusConnection;
1154 DBusMessageIter iterFind, iterUdis;
1155
1156 /* Connect to hal */
1157 rc = halInit (&dbusConnection);
1158 if (!dbusConnection)
1159 halSuccess = false;
1160 /* Get an array of all devices in the usb_device subsystem */
1161 if (halSuccess && RT_SUCCESS (rc))
1162 {
1163 rc = halFindDeviceStringMatch (dbusConnection.get(), "info.subsystem",
1164 "usb_device", &replyFind);
1165 if (!replyFind)
1166 halSuccess = false;
1167 }
1168 if (halSuccess && RT_SUCCESS (rc))
1169 {
1170 dbus_message_iter_init (replyFind.get(), &iterFind);
1171 if (dbus_message_iter_get_arg_type (&iterFind) != DBUS_TYPE_ARRAY)
1172 halSuccess = false;
1173 }
1174 /* Recurse down into the array and query interesting information about the
1175 * entries. */
1176 if (halSuccess && RT_SUCCESS (rc))
1177 dbus_message_iter_recurse (&iterFind, &iterUdis);
1178 for (; halSuccess && RT_SUCCESS (rc)
1179 && dbus_message_iter_get_arg_type (&iterUdis) == DBUS_TYPE_STRING;
1180 dbus_message_iter_next(&iterUdis))
1181 {
1182 /* Get the device node and the sysfs path for the current entry. */
1183 const char *pszUdi;
1184 dbus_message_iter_get_basic (&iterUdis, &pszUdi);
1185 static const char *papszKeys[] = { "linux.device_file", "linux.sysfs_path" };
1186 char *papszValues[RT_ELEMENTS (papszKeys)];
1187 rc = halGetPropertyStrings (dbusConnection.get(), pszUdi, RT_ELEMENTS (papszKeys),
1188 papszKeys, papszValues, &replyGet);
1189 const char *pszDevice = papszValues[0], *pszSysfsPath = papszValues[1];
1190 /* Get the interfaces. */
1191 if (!!replyGet && pszDevice && pszSysfsPath)
1192 {
1193 USBDeviceInfo info (pszDevice, pszSysfsPath);
1194 bool ifaceSuccess = true; /* If we can't get the interfaces, just
1195 * skip this one device. */
1196 rc = getUSBInterfacesFromHal (&info.mInterfaces, pszUdi, &ifaceSuccess);
1197 if (RT_SUCCESS(rc) && halSuccess && ifaceSuccess)
1198 try
1199 {
1200 pList->push_back (info);
1201 }
1202 catch(std::bad_alloc &e)
1203 {
1204 rc = VERR_NO_MEMORY;
1205 }
1206 }
1207 }
1208 if (dbusError.HasName (DBUS_ERROR_NO_MEMORY))
1209 rc = VERR_NO_MEMORY;
1210 if (pfSuccess != NULL)
1211 *pfSuccess = halSuccess;
1212 LogFlow (("rc=%Rrc, halSuccess=%d\n", rc, halSuccess));
1213 dbusError.FlowLog();
1214 return rc;
1215}
1216
1217/**
1218 * Helper function to query the hal subsystem for information about USB devices
1219 * attached to the system, using the older API.
1220 * @returns iprt status code
1221 * @param pList where to add information about the devices detected
1222 * @param pfSuccess will be set to true if all interactions with hal
1223 * succeeded and to false otherwise. Optional.
1224 *
1225 * @returns IPRT status code
1226 */
1227/* static */
1228int getOldUSBDeviceInfoFromHal(USBDeviceInfoList *pList, bool *pfSuccess)
1229{
1230 AssertReturn(VALID_PTR (pList) && (pfSuccess == NULL || VALID_PTR (pfSuccess)),
1231 VERR_INVALID_POINTER);
1232 LogFlowFunc (("pList=%p, pfSuccess=%p\n", pList, pfSuccess));
1233 int rc = VINF_SUCCESS; /* We set this to failure on fatal errors. */
1234 bool halSuccess = true; /* We set this to false to abort the operation. */
1235 autoDBusError dbusError;
1236
1237 RTMemAutoPtr <DBusMessage, VBoxDBusMessageUnref> message, replyFind, replyGet;
1238 RTMemAutoPtr <DBusConnection, VBoxHalShutdown> dbusConnection;
1239 DBusMessageIter iterFind, iterUdis;
1240
1241 /* Connect to hal */
1242 rc = halInit (&dbusConnection);
1243 if (!dbusConnection)
1244 halSuccess = false;
1245 /* Get an array of all devices in the usb_device subsystem */
1246 if (halSuccess && RT_SUCCESS (rc))
1247 {
1248 rc = halFindDeviceStringMatch (dbusConnection.get(), "info.category",
1249 "usbraw", &replyFind);
1250 if (!replyFind)
1251 halSuccess = false;
1252 }
1253 if (halSuccess && RT_SUCCESS (rc))
1254 {
1255 dbus_message_iter_init (replyFind.get(), &iterFind);
1256 if (dbus_message_iter_get_arg_type (&iterFind) != DBUS_TYPE_ARRAY)
1257 halSuccess = false;
1258 }
1259 /* Recurse down into the array and query interesting information about the
1260 * entries. */
1261 if (halSuccess && RT_SUCCESS (rc))
1262 dbus_message_iter_recurse (&iterFind, &iterUdis);
1263 for (; halSuccess && RT_SUCCESS (rc)
1264 && dbus_message_iter_get_arg_type (&iterUdis) == DBUS_TYPE_STRING;
1265 dbus_message_iter_next(&iterUdis))
1266 {
1267 /* Get the device node and the sysfs path for the current entry. */
1268 const char *pszUdi;
1269 dbus_message_iter_get_basic (&iterUdis, &pszUdi);
1270 static const char *papszKeys[] = { "linux.device_file", "info.parent" };
1271 char *papszValues[RT_ELEMENTS (papszKeys)];
1272 rc = halGetPropertyStrings (dbusConnection.get(), pszUdi, RT_ELEMENTS (papszKeys),
1273 papszKeys, papszValues, &replyGet);
1274 const char *pszDevice = papszValues[0], *pszSysfsPath = papszValues[1];
1275 /* Get the interfaces. */
1276 if (!!replyGet && pszDevice && pszSysfsPath)
1277 {
1278 USBDeviceInfo info (pszDevice, pszSysfsPath);
1279 bool ifaceSuccess = false; /* If we can't get the interfaces, just
1280 * skip this one device. */
1281 rc = getUSBInterfacesFromHal (&info.mInterfaces, pszSysfsPath,
1282 &ifaceSuccess);
1283 if (RT_SUCCESS(rc) && halSuccess && ifaceSuccess)
1284 try
1285 {
1286 pList->push_back (info);
1287 }
1288 catch(std::bad_alloc &e)
1289 {
1290 rc = VERR_NO_MEMORY;
1291 }
1292 }
1293 }
1294 if (dbusError.HasName (DBUS_ERROR_NO_MEMORY))
1295 rc = VERR_NO_MEMORY;
1296 if (pfSuccess != NULL)
1297 *pfSuccess = halSuccess;
1298 LogFlow (("rc=%Rrc, halSuccess=%d\n", rc, halSuccess));
1299 dbusError.FlowLog();
1300 return rc;
1301}
1302
1303/**
1304 * Helper function to query the hal subsystem for information about USB devices
1305 * attached to the system.
1306 * @returns iprt status code
1307 * @param pList where to add information about the devices detected. If
1308 * certain interfaces are not found (@a pfFound is false on
1309 * return) this may contain invalid information.
1310 * @param pcszUdi the hal UDI of the device
1311 * @param pfSuccess will be set to true if the operation succeeds and to
1312 * false if it fails for non-critical reasons. Optional.
1313 *
1314 * @returns IPRT status code
1315 */
1316/* static */
1317int getUSBInterfacesFromHal(std::vector <std::string> *pList,
1318 const char *pcszUdi, bool *pfSuccess)
1319{
1320 AssertReturn(VALID_PTR (pList) && VALID_PTR (pcszUdi) &&
1321 (pfSuccess == NULL || VALID_PTR (pfSuccess)),
1322 VERR_INVALID_POINTER);
1323 LogFlowFunc (("pList=%p, pcszUdi=%s, pfSuccess=%p\n", pList, pcszUdi,
1324 pfSuccess));
1325 int rc = VINF_SUCCESS; /* We set this to failure on fatal errors. */
1326 bool halSuccess = true; /* We set this to false to abort the operation. */
1327 autoDBusError dbusError;
1328
1329 RTMemAutoPtr <DBusMessage, VBoxDBusMessageUnref> message, replyFind, replyGet;
1330 RTMemAutoPtr <DBusConnection, VBoxHalShutdown> dbusConnection;
1331 DBusMessageIter iterFind, iterUdis;
1332
1333 rc = halInit (&dbusConnection);
1334 if (!dbusConnection)
1335 halSuccess = false;
1336 if (halSuccess && RT_SUCCESS (rc))
1337 {
1338 /* Look for children of the current UDI. */
1339 rc = halFindDeviceStringMatch (dbusConnection.get(), "info.parent",
1340 pcszUdi, &replyFind);
1341 if (!replyFind)
1342 halSuccess = false;
1343 }
1344 if (halSuccess && RT_SUCCESS (rc))
1345 {
1346 dbus_message_iter_init (replyFind.get(), &iterFind);
1347 if (dbus_message_iter_get_arg_type (&iterFind) != DBUS_TYPE_ARRAY)
1348 halSuccess = false;
1349 }
1350 if (halSuccess && RT_SUCCESS (rc))
1351 dbus_message_iter_recurse (&iterFind, &iterUdis);
1352 for (; halSuccess && RT_SUCCESS (rc)
1353 && dbus_message_iter_get_arg_type (&iterUdis) == DBUS_TYPE_STRING;
1354 dbus_message_iter_next(&iterUdis))
1355 {
1356 /* Now get the sysfs path and the subsystem from the iterator */
1357 const char *pszUdi;
1358 dbus_message_iter_get_basic (&iterUdis, &pszUdi);
1359 static const char *papszKeys[] = { "linux.sysfs_path", "info.subsystem",
1360 "linux.subsystem" };
1361 char *papszValues[RT_ELEMENTS (papszKeys)];
1362 rc = halGetPropertyStrings (dbusConnection.get(), pszUdi, RT_ELEMENTS (papszKeys),
1363 papszKeys, papszValues, &replyGet);
1364 const char *pszSysfsPath = papszValues[0], *pszInfoSubsystem = papszValues[1],
1365 *pszLinuxSubsystem = papszValues[2];
1366 if (!replyGet)
1367 halSuccess = false;
1368 if (!!replyGet && pszSysfsPath == NULL)
1369 halSuccess = false;
1370 if ( halSuccess && RT_SUCCESS (rc)
1371 && RTStrCmp (pszInfoSubsystem, "usb_device") != 0 /* Children of buses can also be devices. */
1372 && RTStrCmp (pszLinuxSubsystem, "usb_device") != 0)
1373 try
1374 {
1375 pList->push_back (pszSysfsPath);
1376 }
1377 catch(std::bad_alloc &e)
1378 {
1379 rc = VERR_NO_MEMORY;
1380 }
1381 }
1382 if (dbusError.HasName (DBUS_ERROR_NO_MEMORY))
1383 rc = VERR_NO_MEMORY;
1384 if (pfSuccess != NULL)
1385 *pfSuccess = halSuccess;
1386 LogFlow (("rc=%Rrc, halSuccess=%d\n", rc, halSuccess));
1387 dbusError.FlowLog();
1388 return rc;
1389}
1390
1391/**
1392 * When it is registered with DBus, this function will be called by
1393 * dbus_connection_read_write_dispatch each time a message is received over the
1394 * DBus connection. We check whether that message was caused by a hal device
1395 * hotplug event, and if so we set a flag. dbus_connection_read_write_dispatch
1396 * will return after calling its filter functions, and its caller should then
1397 * check the status of the flag passed to the filter function.
1398 *
1399 * @param pConnection The DBus connection we are using.
1400 * @param pMessage The DBus message which just arrived.
1401 * @param pvUser A pointer to the flag variable we are to set.
1402 */
1403/* static */
1404DBusHandlerResult dbusFilterFunction (DBusConnection * /* pConnection */,
1405 DBusMessage *pMessage, void *pvUser)
1406{
1407 volatile bool *pTriggered = reinterpret_cast<volatile bool *> (pvUser);
1408 if ( dbus_message_is_signal (pMessage, "org.freedesktop.Hal.Manager",
1409 "DeviceAdded")
1410 || dbus_message_is_signal (pMessage, "org.freedesktop.Hal.Manager",
1411 "DeviceRemoved"))
1412 {
1413 *pTriggered = true;
1414 }
1415 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1416}
1417#endif /* RT_OS_LINUX && VBOX_WITH_DBUS */
1418
Note: See TracBrowser for help on using the repository browser.

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