VirtualBox

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

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

Main/HostHardwareLinux: test host floppy drives using an ioctl, not a property which wasn't present in sysfs in older kernels

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 68.7 KB
Line 
1/* $Id: HostHardwareLinux.cpp 23382 2009-09-28 14:04:05Z 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/dir.h>
35#include <iprt/env.h>
36#include <iprt/file.h>
37#include <iprt/mem.h>
38#include <iprt/param.h>
39#include <iprt/thread.h> /* for RTThreadSleep() */
40#include <iprt/string.h>
41
42#ifdef RT_OS_LINUX
43# include <sys/types.h>
44# include <sys/stat.h>
45# include <unistd.h>
46# include <sys/ioctl.h>
47# include <fcntl.h>
48# include <mntent.h>
49/* bird: This is a hack to work around conflicts between these linux kernel headers
50 * and the GLIBC tcpip headers. They have different declarations of the 4
51 * standard byte order functions. */
52// # define _LINUX_BYTEORDER_GENERIC_H
53# define _LINUX_BYTEORDER_SWABB_H
54# include <linux/cdrom.h>
55# include <linux/fd.h>
56# ifdef VBOX_WITH_DBUS
57# include <vbox-dbus.h>
58# endif
59# include <errno.h>
60# include <scsi/scsi.h>
61# include <scsi/sg.h>
62
63# include <iprt/linux/sysfs.h>
64#endif /* RT_OS_LINUX */
65#include <vector>
66
67/*******************************************************************************
68* Global Variables *
69*******************************************************************************/
70
71bool g_testHostHardwareLinux = false;
72static bool testing () { return g_testHostHardwareLinux; }
73
74/*******************************************************************************
75* Typedefs and Defines *
76*******************************************************************************/
77
78/** When waiting for hotplug events, we currently restart the wait after at
79 * most this many milliseconds. */
80enum { DBUS_POLL_TIMEOUT = 2000 /* ms */ };
81
82
83static bool validateDevice(const char *deviceNode, bool isDVD);
84static int getDriveInfoFromEnv(const char *pszVar, DriveInfoList *pList,
85 bool isDVD, bool *pfSuccess);
86static int getDriveInfoFromSysfs(DriveInfoList *pList, bool isDVD,
87 bool *pfSuccess);
88int scsiDoInquiry(const char *pszNode, uint8_t *pu8Type, char *pchVendor,
89 size_t cchVendor, char *pchModel, size_t cchModel);
90static int getDVDInfoFromMTab(char *mountTable, DriveInfoList *pList);
91#ifdef VBOX_WITH_DBUS
92/* These must be extern to be usable in the RTMemAutoPtr template */
93extern void VBoxHalShutdown (DBusConnection *pConnection);
94extern void VBoxHalShutdownPrivate (DBusConnection *pConnection);
95extern void VBoxDBusConnectionUnref(DBusConnection *pConnection);
96extern void VBoxDBusConnectionCloseAndUnref(DBusConnection *pConnection);
97extern void VBoxDBusMessageUnref(DBusMessage *pMessage);
98
99static int halInit(RTMemAutoPtr <DBusConnection, VBoxHalShutdown> *pConnection);
100static int halInitPrivate(RTMemAutoPtr <DBusConnection, VBoxHalShutdownPrivate> *pConnection);
101static int halFindDeviceStringMatch (DBusConnection *pConnection,
102 const char *pszKey, const char *pszValue,
103 RTMemAutoPtr <DBusMessage, VBoxDBusMessageUnref> *pMessage);
104/*
105static int halFindDeviceStringMatchVector (DBusConnection *pConnection,
106 const char *pszKey,
107 const char *pszValue,
108 std::vector<iprt::MiniString> *pMatches);
109*/
110static int halGetPropertyStrings (DBusConnection *pConnection,
111 const char *pszUdi, size_t cKeys,
112 const char **papszKeys, char **papszValues,
113 RTMemAutoPtr <DBusMessage, VBoxDBusMessageUnref> *pMessage);
114/*
115static int halGetPropertyStringsVector (DBusConnection *pConnection,
116 const char *pszUdi, size_t cProps,
117 const char **papszKeys,
118 std::vector<iprt::MiniString> *pMatches,
119 bool *pfMatches, bool *pfSuccess);
120*/
121static int getDriveInfoFromHal(DriveInfoList *pList, bool isDVD,
122 bool *pfSuccess);
123static int getUSBDeviceInfoFromHal(USBDeviceInfoList *pList, bool *pfSuccess);
124static int getOldUSBDeviceInfoFromHal(USBDeviceInfoList *pList, bool *pfSuccess);
125static int getUSBInterfacesFromHal(std::vector <iprt::MiniString> *pList,
126 const char *pcszUdi, bool *pfSuccess);
127static DBusHandlerResult dbusFilterFunction (DBusConnection *pConnection,
128 DBusMessage *pMessage, void *pvUser);
129#endif /* VBOX_WITH_DBUS */
130
131/** Find the length of a string, ignoring trailing non-ascii or control
132 * characters */
133static size_t strLenStripped(const char *psz)
134{
135 size_t cch = 0;
136 for (size_t i = 0; psz[i] != '\0'; ++i)
137 if (psz[i] > 32 && psz[i] < 127)
138 cch = i;
139 return cch + 1;
140}
141
142int VBoxMainDriveInfo::updateDVDs ()
143{
144 LogFlowThisFunc(("entered\n"));
145 int rc = VINF_SUCCESS;
146 bool success = false; /* Have we succeeded in finding anything yet? */
147 try
148 {
149 mDVDList.clear ();
150#if defined(RT_OS_LINUX)
151 /* Always allow the user to override our auto-detection using an
152 * environment variable. */
153 if (RT_SUCCESS(rc) && (!success || testing()))
154 rc = getDriveInfoFromEnv ("VBOX_CDROM", &mDVDList, true /* isDVD */,
155 &success);
156#ifdef VBOX_WITH_DBUS
157 if (RT_SUCCESS(rc) && RT_SUCCESS(VBoxLoadDBusLib()) && (!success || testing()))
158 rc = getDriveInfoFromHal(&mDVDList, true /* isDVD */, &success);
159#endif /* VBOX_WITH_DBUS defined */
160 if (RT_SUCCESS(rc) && (!success | testing()))
161 rc = getDriveInfoFromSysfs(&mDVDList, true /* isDVD */, &success);
162 /* On Linux without hal, the situation is much more complex. We will
163 * take a heuristical approach. The general strategy is to try some
164 * known device names and see of they exist. Failing that, we
165 * enumerate the /etc/fstab file (luckily there's an API to parse it)
166 * for CDROM devices. Ok, let's start! */
167 if (RT_SUCCESS(rc) && (!success || testing()))
168 {
169 // this is a good guess usually
170 if (validateDevice("/dev/cdrom", true))
171 mDVDList.push_back(DriveInfo ("/dev/cdrom"));
172
173 // check the mounted drives
174 rc = getDVDInfoFromMTab((char*)"/etc/mtab", &mDVDList);
175
176 // check the drives that can be mounted
177 if (RT_SUCCESS(rc))
178 rc = getDVDInfoFromMTab((char*)"/etc/fstab", &mDVDList);
179 }
180#endif
181 }
182 catch(std::bad_alloc &e)
183 {
184 rc = VERR_NO_MEMORY;
185 }
186 LogFlowThisFunc(("rc=%Rrc\n", rc));
187 return rc;
188}
189
190int VBoxMainDriveInfo::updateFloppies ()
191{
192 LogFlowThisFunc(("entered\n"));
193 int rc = VINF_SUCCESS;
194 bool success = false; /* Have we succeeded in finding anything yet? */
195 try
196 {
197 mFloppyList.clear ();
198#if defined(RT_OS_LINUX)
199 if (RT_SUCCESS(rc) && (!success || testing()))
200 rc = getDriveInfoFromEnv ("VBOX_FLOPPY", &mFloppyList, false /* isDVD */,
201 &success);
202#ifdef VBOX_WITH_DBUS
203 if ( RT_SUCCESS(rc)
204 && RT_SUCCESS(VBoxLoadDBusLib())
205 && (!success || testing()))
206 rc = getDriveInfoFromHal(&mFloppyList, false /* isDVD */, &success);
207#endif /* VBOX_WITH_DBUS defined */
208 if ( RT_SUCCESS(rc)
209 && RT_SUCCESS(VBoxLoadDBusLib())
210 && (!success || testing()))
211 rc = getDriveInfoFromHal(&mFloppyList, false /* isDVD */, &success);
212 /* As with the CDROMs, on Linux we have to take a multi-level approach
213 * involving parsing the mount tables. As this is not bulletproof, we
214 * give the user the chance to override the detection using an
215 * environment variable, skiping the detection. */
216
217 if (RT_SUCCESS(rc) && (!success || testing()))
218 {
219 // we assume that a floppy is always /dev/fd[x] with x from 0 to 7
220 char devName[10];
221 for (int i = 0; i <= 7; i++)
222 {
223 RTStrPrintf(devName, sizeof(devName), "/dev/fd%d", i);
224 if (validateDevice(devName, false))
225 mFloppyList.push_back (DriveInfo (devName));
226 }
227 }
228#endif
229 }
230 catch(std::bad_alloc &e)
231 {
232 rc = VERR_NO_MEMORY;
233 }
234 LogFlowThisFunc(("rc=%Rrc\n", rc));
235 return rc;
236}
237
238int VBoxMainUSBDeviceInfo::UpdateDevices ()
239{
240 LogFlowThisFunc(("entered\n"));
241 int rc = VINF_SUCCESS;
242 bool success = false; /* Have we succeeded in finding anything yet? */
243 try
244 {
245 bool halSuccess = false;
246 mDeviceList.clear();
247#if defined(RT_OS_LINUX)
248#ifdef VBOX_WITH_DBUS
249 if ( RT_SUCCESS(rc)
250 && RT_SUCCESS(VBoxLoadDBusLib())
251 && (!success || testing()))
252 rc = getUSBDeviceInfoFromHal(&mDeviceList, &halSuccess);
253 /* Try the old API if the new one *succeeded* as only one of them will
254 * pick up devices anyway. */
255 if (RT_SUCCESS(rc) && halSuccess && (!success || testing()))
256 rc = getOldUSBDeviceInfoFromHal(&mDeviceList, &halSuccess);
257 if (!success)
258 success = halSuccess;
259#endif /* VBOX_WITH_DBUS defined */
260#endif /* RT_OS_LINUX */
261 }
262 catch(std::bad_alloc &e)
263 {
264 rc = VERR_NO_MEMORY;
265 }
266 LogFlowThisFunc(("rc=%Rrc\n", rc));
267 return rc;
268}
269
270struct VBoxMainHotplugWaiter::Context
271{
272#if defined RT_OS_LINUX && defined VBOX_WITH_DBUS
273 /** The connection to DBus */
274 RTMemAutoPtr <DBusConnection, VBoxHalShutdownPrivate> mConnection;
275 /** Semaphore which is set when a device is hotplugged and reset when
276 * it is read. */
277 volatile bool mTriggered;
278 /** A flag to say that we wish to interrupt the current wait. */
279 volatile bool mInterrupt;
280 /** Constructor */
281 Context() : mTriggered(false), mInterrupt(false) {}
282#endif /* defined RT_OS_LINUX && defined VBOX_WITH_DBUS */
283};
284
285/* This constructor sets up a private connection to the DBus daemon, connects
286 * to the hal service and installs a filter which sets the mTriggered flag in
287 * the Context structure when a device (not necessarily USB) is added or
288 * removed. */
289VBoxMainHotplugWaiter::VBoxMainHotplugWaiter ()
290{
291#if defined RT_OS_LINUX && defined VBOX_WITH_DBUS
292 int rc = VINF_SUCCESS;
293
294 mContext = new Context;
295 if (RT_SUCCESS(VBoxLoadDBusLib()))
296 {
297 for (unsigned i = 0; RT_SUCCESS(rc) && i < 5 && !mContext->mConnection; ++i)
298 {
299 rc = halInitPrivate (&mContext->mConnection);
300 }
301 if (!mContext->mConnection)
302 rc = VERR_NOT_SUPPORTED;
303 DBusMessage *pMessage;
304 while ( RT_SUCCESS(rc)
305 && (pMessage = dbus_connection_pop_message (mContext->mConnection.get())) != NULL)
306 dbus_message_unref (pMessage); /* empty the message queue. */
307 if ( RT_SUCCESS(rc)
308 && !dbus_connection_add_filter (mContext->mConnection.get(),
309 dbusFilterFunction,
310 (void *) &mContext->mTriggered, NULL))
311 rc = VERR_NO_MEMORY;
312 if (RT_FAILURE(rc))
313 mContext->mConnection.reset();
314 }
315#endif /* defined RT_OS_LINUX && defined VBOX_WITH_DBUS */
316}
317
318/* Destructor */
319VBoxMainHotplugWaiter::~VBoxMainHotplugWaiter ()
320{
321#if defined RT_OS_LINUX && defined VBOX_WITH_DBUS
322 if (!!mContext->mConnection)
323 dbus_connection_remove_filter (mContext->mConnection.get(), dbusFilterFunction,
324 (void *) &mContext->mTriggered);
325 delete mContext;
326#endif /* defined RT_OS_LINUX && defined VBOX_WITH_DBUS */
327}
328
329/* Currently this is implemented using a timed out wait on our private DBus
330 * connection. Because the connection is private we don't have to worry about
331 * blocking other users. */
332int VBoxMainHotplugWaiter::Wait(unsigned cMillies)
333{
334 int rc = VINF_SUCCESS;
335#if defined RT_OS_LINUX && defined VBOX_WITH_DBUS
336 if (!mContext->mConnection)
337 rc = VERR_NOT_SUPPORTED;
338 bool connected = true;
339 mContext->mTriggered = false;
340 mContext->mInterrupt = false;
341 unsigned cRealMillies;
342 if (cMillies != RT_INDEFINITE_WAIT)
343 cRealMillies = cMillies;
344 else
345 cRealMillies = DBUS_POLL_TIMEOUT;
346 while ( RT_SUCCESS(rc) && connected && !mContext->mTriggered
347 && !mContext->mInterrupt)
348 {
349 connected = dbus_connection_read_write_dispatch (mContext->mConnection.get(),
350 cRealMillies);
351 if (mContext->mInterrupt)
352 LogFlowFunc(("wait loop interrupted\n"));
353 if (cMillies != RT_INDEFINITE_WAIT)
354 mContext->mInterrupt = true;
355 }
356 if (!connected)
357 rc = VERR_TRY_AGAIN;
358#else /* !(defined RT_OS_LINUX && defined VBOX_WITH_DBUS) */
359 rc = VERR_NOT_IMPLEMENTED;
360#endif /* !(defined RT_OS_LINUX && defined VBOX_WITH_DBUS) */
361 return rc;
362}
363
364/* Set a flag to tell the Wait not to resume next time it times out. */
365void VBoxMainHotplugWaiter::Interrupt()
366{
367#if defined RT_OS_LINUX && defined VBOX_WITH_DBUS
368 LogFlowFunc(("\n"));
369 mContext->mInterrupt = true;
370#endif /* defined RT_OS_LINUX && defined VBOX_WITH_DBUS */
371}
372
373#ifdef RT_OS_LINUX
374/**
375 * Helper function to check whether the given device node is a valid drive
376 */
377/* static */
378bool validateDevice(const char *deviceNode, bool isDVD)
379{
380 AssertReturn(VALID_PTR (deviceNode), VERR_INVALID_POINTER);
381 LogFlowFunc (("deviceNode=%s, isDVD=%d\n", deviceNode, isDVD));
382 struct stat statInfo;
383 bool retValue = false;
384
385 // sanity check
386 if (!deviceNode)
387 {
388 return false;
389 }
390
391 // first a simple stat() call
392 if (stat(deviceNode, &statInfo) < 0)
393 {
394 return false;
395 } else
396 {
397 if (isDVD)
398 {
399 if (S_ISCHR(statInfo.st_mode) || S_ISBLK(statInfo.st_mode))
400 {
401 int fileHandle;
402 // now try to open the device
403 fileHandle = open(deviceNode, O_RDONLY | O_NONBLOCK, 0);
404 if (fileHandle >= 0)
405 {
406 cdrom_subchnl cdChannelInfo;
407 cdChannelInfo.cdsc_format = CDROM_MSF;
408 // this call will finally reveal the whole truth
409#ifdef RT_OS_LINUX
410 if ((ioctl(fileHandle, CDROMSUBCHNL, &cdChannelInfo) == 0) ||
411 (errno == EIO) || (errno == ENOENT) ||
412 (errno == EINVAL) || (errno == ENOMEDIUM))
413#endif
414 {
415 retValue = true;
416 }
417 close(fileHandle);
418 }
419 }
420 } else
421 {
422 // floppy case
423 if (S_ISCHR(statInfo.st_mode) || S_ISBLK(statInfo.st_mode))
424 {
425 /// @todo do some more testing, maybe a nice IOCTL!
426 retValue = true;
427 }
428 }
429 }
430 LogFlowFunc (("retValue=%d\n", retValue));
431 return retValue;
432}
433#else /* !RT_OS_LINUX */
434# error Port me! Copying code over from HostImpl.cpp should be most of the job though.
435#endif /* !RT_OS_LINUX */
436
437/**
438 * Extract the names of drives from an environment variable and add them to a
439 * list if they are valid.
440 * @returns iprt status code
441 * @param pszVar the name of the environment variable. The variable
442 * value should be a list of device node names, separated
443 * by ':' characters.
444 * @param pList the list to append the drives found to
445 * @param isDVD are we looking for DVD drives or for floppies?
446 * @param pfSuccess this will be set to true if we found at least one drive
447 * and to false otherwise. Optional.
448 */
449/* static */
450int getDriveInfoFromEnv(const char *pszVar, DriveInfoList *pList,
451 bool isDVD, bool *pfSuccess)
452{
453 AssertReturn( VALID_PTR (pszVar) && VALID_PTR (pList)
454 && (pfSuccess == NULL || VALID_PTR (pfSuccess)),
455 VERR_INVALID_POINTER);
456 LogFlowFunc (("pszVar=%s, pList=%p, isDVD=%d, pfSuccess=%p\n", pszVar,
457 pList, isDVD, pfSuccess));
458 int rc = VINF_SUCCESS;
459 bool success = false;
460
461 try
462 {
463 RTMemAutoPtr<char, RTStrFree> drive;
464 const char *pszValue = RTEnvGet (pszVar);
465 if (pszValue != NULL)
466 {
467 drive = RTStrDup (pszValue);
468 if (!drive)
469 rc = VERR_NO_MEMORY;
470 }
471 if (pszValue != NULL && RT_SUCCESS(rc))
472 {
473 char *pDrive = drive.get();
474 char *pDriveNext = strchr (pDrive, ':');
475 while (pDrive != NULL && *pDrive != '\0')
476 {
477 if (pDriveNext != NULL)
478 *pDriveNext = '\0';
479 if (validateDevice(pDrive, isDVD))
480 {
481 pList->push_back (DriveInfo (pDrive));
482 success = true;
483 }
484 if (pDriveNext != NULL)
485 {
486 pDrive = pDriveNext + 1;
487 pDriveNext = strchr (pDrive, ':');
488 }
489 else
490 pDrive = NULL;
491 }
492 }
493 if (pfSuccess != NULL)
494 *pfSuccess = success;
495 }
496 catch(std::bad_alloc &e)
497 {
498 rc = VERR_NO_MEMORY;
499 }
500 LogFlowFunc (("rc=%Rrc, success=%d\n", rc, success));
501 return rc;
502}
503
504#ifdef RT_OS_LINUX
505/**
506 * Helper function to parse the given mount file and add found entries
507 */
508/* static */
509int getDVDInfoFromMTab(char *mountTable, DriveInfoList *pList)
510{
511 AssertReturn(VALID_PTR (mountTable) && VALID_PTR (pList),
512 VERR_INVALID_POINTER);
513#ifdef RT_OS_LINUX
514 LogFlowFunc (("mountTable=%s, pList=%p\n", mountTable, pList));
515 int rc = VINF_SUCCESS;
516 FILE *mtab = setmntent(mountTable, "r");
517 if (mtab)
518 {
519 try
520 {
521 struct mntent *mntent;
522 RTMemAutoPtr <char, RTStrFree> mnt_type, mnt_dev;
523 char *tmp;
524 while (RT_SUCCESS(rc) && (mntent = getmntent(mtab)))
525 {
526 mnt_type = RTStrDup (mntent->mnt_type);
527 mnt_dev = RTStrDup (mntent->mnt_fsname);
528 if (!mnt_type || !mnt_dev)
529 rc = VERR_NO_MEMORY;
530 // supermount fs case
531 if (RT_SUCCESS(rc) && strcmp(mnt_type.get(), "supermount") == 0)
532 {
533 tmp = strstr(mntent->mnt_opts, "fs=");
534 if (tmp)
535 {
536 mnt_type = RTStrDup(tmp + strlen("fs="));
537 if (!mnt_type)
538 rc = VERR_NO_MEMORY;
539 else
540 {
541 tmp = strchr(mnt_type.get(), ',');
542 if (tmp)
543 *tmp = '\0';
544 }
545 }
546 tmp = strstr(mntent->mnt_opts, "dev=");
547 if (tmp)
548 {
549 mnt_dev = RTStrDup(tmp + strlen("dev="));
550 if (!mnt_dev)
551 rc = VERR_NO_MEMORY;
552 else
553 {
554 tmp = strchr(mnt_dev.get(), ',');
555 if (tmp)
556 *tmp = '\0';
557 }
558 }
559 }
560 // use strstr here to cover things fs types like "udf,iso9660"
561 if (RT_SUCCESS(rc) && strstr(mnt_type.get(), "iso9660") == 0)
562 {
563 if (validateDevice(mnt_dev.get(), true))
564 {
565 bool insert = true;
566 struct stat srcInfo;
567 if (stat (mnt_dev.get(), &srcInfo) < 0)
568 insert = false;
569 for (DriveInfoList::const_iterator it = pList->begin();
570 insert && it != pList->end(); ++it)
571 {
572 struct stat destInfo;
573 if ( (stat (it->mDevice.c_str(), &destInfo) == 0)
574 && (srcInfo.st_rdev == destInfo.st_rdev))
575 insert = false;
576 }
577 if (insert)
578 pList->push_back (DriveInfo (mnt_dev.get()));
579 }
580 }
581 }
582 }
583 catch(std::bad_alloc &e)
584 {
585 rc = VERR_NO_MEMORY;
586 }
587 endmntent(mtab);
588 }
589 return rc;
590#endif
591}
592
593#endif /* RT_OS_LINUX */
594
595#if defined(RT_OS_LINUX) && defined(VBOX_WITH_DBUS)
596/** Wrapper class around DBusError for automatic cleanup */
597class autoDBusError
598{
599 DBusError mError;
600public:
601 autoDBusError () { dbus_error_init (&mError); }
602 ~autoDBusError ()
603 {
604 if (IsSet())
605 dbus_error_free (&mError);
606 }
607 DBusError &get () { return mError; }
608 bool IsSet ()
609 {
610 Assert ((mError.name == NULL) == (mError.message == NULL));
611 return (mError.name != NULL);
612 }
613 bool HasName (const char *pszName)
614 {
615 Assert ((mError.name == NULL) == (mError.message == NULL));
616 return (RTStrCmp (mError.name, pszName) == 0);
617 }
618 void FlowLog ()
619 {
620 if (IsSet ())
621 LogFlow(("DBus error %s: %s\n", mError.name, mError.message));
622 }
623};
624
625/**
626 * Helper function for setting up a connection to the DBus daemon and
627 * registering with the hal service.
628 *
629 * @note If libdbus is being loaded at runtime then be sure to call
630 * VBoxDBusCheckPresence before calling this.
631 * @returns iprt status code
632 * @param ppConnection where to store the connection handle
633 */
634/* static */
635int halInit (RTMemAutoPtr <DBusConnection, VBoxHalShutdown> *pConnection)
636{
637 AssertReturn(VALID_PTR (pConnection), VERR_INVALID_POINTER);
638 LogFlowFunc (("pConnection=%p\n", pConnection));
639 int rc = VINF_SUCCESS;
640 bool halSuccess = true;
641 autoDBusError dbusError;
642
643 RTMemAutoPtr <DBusConnection, VBoxDBusConnectionUnref> dbusConnection;
644 dbusConnection = dbus_bus_get (DBUS_BUS_SYSTEM, &dbusError.get());
645 if (!dbusConnection)
646 halSuccess = false;
647 if (halSuccess)
648 {
649 dbus_connection_set_exit_on_disconnect (dbusConnection.get(), false);
650 halSuccess = dbus_bus_name_has_owner (dbusConnection.get(),
651 "org.freedesktop.Hal", &dbusError.get());
652 }
653 if (halSuccess)
654 {
655 dbus_bus_add_match (dbusConnection.get(),
656 "type='signal',"
657 "interface='org.freedesktop.Hal.Manager',"
658 "sender='org.freedesktop.Hal',"
659 "path='/org/freedesktop/Hal/Manager'",
660 &dbusError.get());
661 halSuccess = !dbusError.IsSet();
662 }
663 if (dbusError.HasName (DBUS_ERROR_NO_MEMORY))
664 rc = VERR_NO_MEMORY;
665 if (halSuccess)
666 *pConnection = dbusConnection.release();
667 LogFlowFunc(("rc=%Rrc, (*pConnection).get()=%p\n", rc, (*pConnection).get()));
668 dbusError.FlowLog();
669 return rc;
670}
671
672/**
673 * Helper function for setting up a private connection to the DBus daemon and
674 * registering with the hal service. Private connections are considered
675 * unsociable and should not be used unnecessarily (as per the DBus API docs).
676 *
677 * @note If libdbus is being loaded at runtime then be sure to call
678 * VBoxDBusCheckPresence before calling this.
679 * @returns iprt status code
680 * @param pConnection where to store the connection handle
681 */
682/* static */
683int halInitPrivate (RTMemAutoPtr <DBusConnection, VBoxHalShutdownPrivate> *pConnection)
684{
685 AssertReturn(VALID_PTR (pConnection), VERR_INVALID_POINTER);
686 LogFlowFunc (("pConnection=%p\n", pConnection));
687 int rc = VINF_SUCCESS;
688 bool halSuccess = true;
689 autoDBusError dbusError;
690
691 RTMemAutoPtr <DBusConnection, VBoxDBusConnectionCloseAndUnref> dbusConnection;
692 dbusConnection = dbus_bus_get_private (DBUS_BUS_SYSTEM, &dbusError.get());
693 if (!dbusConnection)
694 halSuccess = false;
695 if (halSuccess)
696 {
697 dbus_connection_set_exit_on_disconnect (dbusConnection.get(), false);
698 halSuccess = dbus_bus_name_has_owner (dbusConnection.get(),
699 "org.freedesktop.Hal", &dbusError.get());
700 }
701 if (halSuccess)
702 {
703 dbus_bus_add_match (dbusConnection.get(),
704 "type='signal',"
705 "interface='org.freedesktop.Hal.Manager',"
706 "sender='org.freedesktop.Hal',"
707 "path='/org/freedesktop/Hal/Manager'",
708 &dbusError.get());
709 halSuccess = !dbusError.IsSet();
710 }
711 if (dbusError.HasName (DBUS_ERROR_NO_MEMORY))
712 rc = VERR_NO_MEMORY;
713 if (halSuccess)
714 *pConnection = dbusConnection.release();
715 LogFlowFunc(("rc=%Rrc, (*pConnection).get()=%p\n", rc, (*pConnection).get()));
716 dbusError.FlowLog();
717 return rc;
718}
719
720/**
721 * Helper function for shutting down a connection to DBus and hal.
722 * @param pConnection the connection handle
723 */
724/* extern */
725void VBoxHalShutdown (DBusConnection *pConnection)
726{
727 AssertReturnVoid(VALID_PTR (pConnection));
728 LogFlowFunc (("pConnection=%p\n", pConnection));
729 autoDBusError dbusError;
730
731 dbus_bus_remove_match (pConnection,
732 "type='signal',"
733 "interface='org.freedesktop.Hal.Manager',"
734 "sender='org.freedesktop.Hal',"
735 "path='/org/freedesktop/Hal/Manager'",
736 &dbusError.get());
737 dbus_connection_unref (pConnection);
738 LogFlowFunc(("returning\n"));
739 dbusError.FlowLog();
740}
741
742/**
743 * Helper function for shutting down a private connection to DBus and hal.
744 * @param pConnection the connection handle
745 */
746/* extern */
747void VBoxHalShutdownPrivate (DBusConnection *pConnection)
748{
749 AssertReturnVoid(VALID_PTR (pConnection));
750 LogFlowFunc (("pConnection=%p\n", pConnection));
751 autoDBusError dbusError;
752
753 dbus_bus_remove_match (pConnection,
754 "type='signal',"
755 "interface='org.freedesktop.Hal.Manager',"
756 "sender='org.freedesktop.Hal',"
757 "path='/org/freedesktop/Hal/Manager'",
758 &dbusError.get());
759 dbus_connection_close (pConnection);
760 dbus_connection_unref (pConnection);
761 LogFlowFunc(("returning\n"));
762 dbusError.FlowLog();
763}
764
765/** Wrapper around dbus_connection_unref. We need this to use it as a real
766 * function in auto pointers, as a function pointer won't wash here. */
767/* extern */
768void VBoxDBusConnectionUnref(DBusConnection *pConnection)
769{
770 dbus_connection_unref(pConnection);
771}
772
773/**
774 * This function closes and unrefs a private connection to dbus. It should
775 * only be called once no-one else is referencing the connection.
776 */
777/* extern */
778void VBoxDBusConnectionCloseAndUnref(DBusConnection *pConnection)
779{
780 dbus_connection_close(pConnection);
781 dbus_connection_unref(pConnection);
782}
783
784/** Wrapper around dbus_message_unref. We need this to use it as a real
785 * function in auto pointers, as a function pointer won't wash here. */
786/* extern */
787void VBoxDBusMessageUnref(DBusMessage *pMessage)
788{
789 dbus_message_unref(pMessage);
790}
791
792/**
793 * Find the UDIs of hal entries that contain Key=Value property.
794 * @returns iprt status code. If a non-fatal error occurs, we return success
795 * but reset pMessage to NULL.
796 * @param pConnection an initialised connection DBus
797 * @param pszKey the property key
798 * @param pszValue the property value
799 * @param pMessage where to store the return DBus message. This must be
800 * parsed to get at the UDIs. NOT optional.
801 */
802/* static */
803int halFindDeviceStringMatch (DBusConnection *pConnection, const char *pszKey,
804 const char *pszValue,
805 RTMemAutoPtr <DBusMessage, VBoxDBusMessageUnref> *pMessage)
806{
807 AssertReturn( VALID_PTR (pConnection) && VALID_PTR (pszKey)
808 && VALID_PTR (pszValue) && VALID_PTR (pMessage),
809 VERR_INVALID_POINTER);
810 LogFlowFunc (("pConnection=%p, pszKey=%s, pszValue=%s, pMessage=%p\n",
811 pConnection, pszKey, pszValue, pMessage));
812 int rc = VINF_SUCCESS; /* We set this to failure on fatal errors. */
813 bool halSuccess = true; /* We set this to false to abort the operation. */
814 autoDBusError dbusError;
815
816 RTMemAutoPtr <DBusMessage, VBoxDBusMessageUnref> message, reply;
817 if (halSuccess && RT_SUCCESS(rc))
818 {
819 message = dbus_message_new_method_call ("org.freedesktop.Hal",
820 "/org/freedesktop/Hal/Manager",
821 "org.freedesktop.Hal.Manager",
822 "FindDeviceStringMatch");
823 if (!message)
824 rc = VERR_NO_MEMORY;
825 }
826 if (halSuccess && RT_SUCCESS(rc))
827 {
828 DBusMessageIter iterAppend;
829 dbus_message_iter_init_append (message.get(), &iterAppend);
830 dbus_message_iter_append_basic (&iterAppend, DBUS_TYPE_STRING, &pszKey);
831 dbus_message_iter_append_basic (&iterAppend, DBUS_TYPE_STRING, &pszValue);
832 reply = dbus_connection_send_with_reply_and_block (pConnection,
833 message.get(), -1,
834 &dbusError.get());
835 if (!reply)
836 halSuccess = false;
837 }
838 *pMessage = reply.release ();
839 LogFlowFunc (("rc=%Rrc, *pMessage.value()=%p\n", rc, (*pMessage).get()));
840 dbusError.FlowLog();
841 return rc;
842}
843
844/**
845 * Find the UDIs of hal entries that contain Key=Value property and return the
846 * result on the end of a vector of iprt::MiniString.
847 * @returns iprt status code. If a non-fatal error occurs, we return success
848 * but set *pfSuccess to false.
849 * @param pConnection an initialised connection DBus
850 * @param pszKey the property key
851 * @param pszValue the property value
852 * @param pMatches pointer to an array of iprt::MiniString to append the
853 * results to. NOT optional.
854 * @param pfSuccess will be set to true if the operation succeeds
855 */
856/* static */
857int halFindDeviceStringMatchVector (DBusConnection *pConnection,
858 const char *pszKey, const char *pszValue,
859 std::vector<iprt::MiniString> *pMatches,
860 bool *pfSuccess)
861{
862 AssertPtrReturn (pConnection, VERR_INVALID_POINTER);
863 AssertPtrReturn (pszKey, VERR_INVALID_POINTER);
864 AssertPtrReturn (pszValue, VERR_INVALID_POINTER);
865 AssertPtrReturn (pMatches, VERR_INVALID_POINTER);
866 AssertReturn(pfSuccess == NULL || VALID_PTR (pfSuccess), VERR_INVALID_POINTER);
867 LogFlowFunc (("pConnection=%p, pszKey=%s, pszValue=%s, pMatches=%p, pfSuccess=%p\n",
868 pConnection, pszKey, pszValue, pMatches, pfSuccess));
869 int rc = VINF_SUCCESS; /* We set this to failure on fatal errors. */
870 bool halSuccess = true; /* We set this to false to abort the operation. */
871
872 RTMemAutoPtr <DBusMessage, VBoxDBusMessageUnref> message, replyFind;
873 DBusMessageIter iterFind, iterUdis;
874
875 if (halSuccess && RT_SUCCESS(rc))
876 {
877 rc = halFindDeviceStringMatch (pConnection, pszKey, pszValue,
878 &replyFind);
879 if (!replyFind)
880 halSuccess = false;
881 }
882 if (halSuccess && RT_SUCCESS(rc))
883 {
884 dbus_message_iter_init (replyFind.get(), &iterFind);
885 if (dbus_message_iter_get_arg_type (&iterFind) != DBUS_TYPE_ARRAY)
886 halSuccess = false;
887 }
888 if (halSuccess && RT_SUCCESS(rc))
889 dbus_message_iter_recurse (&iterFind, &iterUdis);
890 for (; halSuccess && RT_SUCCESS(rc)
891 && dbus_message_iter_get_arg_type (&iterUdis) == DBUS_TYPE_STRING;
892 dbus_message_iter_next(&iterUdis))
893 {
894 /* Now get all UDIs from the iterator */
895 const char *pszUdi;
896 dbus_message_iter_get_basic (&iterUdis, &pszUdi);
897 try
898 {
899 pMatches->push_back(pszUdi);
900 }
901 catch(std::bad_alloc &e)
902 {
903 rc = VERR_NO_MEMORY;
904 }
905 }
906 if (pfSuccess != NULL)
907 *pfSuccess = halSuccess;
908 LogFlow (("rc=%Rrc, halSuccess=%d\n", rc, halSuccess));
909 return rc;
910}
911
912/**
913 * Read a set of string properties for a device. If some of the properties are
914 * not of type DBUS_TYPE_STRING or do not exist then a NULL pointer will be
915 * returned for them.
916 * @returns iprt status code. If the operation failed for non-fatal reasons
917 * then we return success and leave pMessage untouched - reset it
918 * before the call to detect this.
919 * @param pConnection an initialised connection DBus
920 * @param pszUdi the Udi of the device
921 * @param cProps the number of property values to look up
922 * @param papszKeys the keys of the properties to be looked up
923 * @param papszValues where to store the values of the properties. The
924 * strings returned will be valid until the message
925 * returned in @a ppMessage is freed. Undefined if
926 * the message is NULL.
927 * @param pMessage where to store the return DBus message. The caller
928 * is responsible for freeing this once they have
929 * finished with the value strings. NOT optional.
930 */
931/* static */
932int halGetPropertyStrings (DBusConnection *pConnection, const char *pszUdi,
933 size_t cProps, const char **papszKeys,
934 char **papszValues,
935 RTMemAutoPtr <DBusMessage, VBoxDBusMessageUnref> *pMessage)
936{
937 AssertReturn( VALID_PTR (pConnection) && VALID_PTR (pszUdi)
938 && VALID_PTR (papszKeys) && VALID_PTR (papszValues)
939 && VALID_PTR (pMessage),
940 VERR_INVALID_POINTER);
941 LogFlowFunc (("pConnection=%p, pszUdi=%s, cProps=%llu, papszKeys=%p, papszValues=%p, pMessage=%p\n",
942 pConnection, pszUdi, cProps, papszKeys, papszValues, pMessage));
943 int rc = VINF_SUCCESS; /* We set this to failure on fatal errors. */
944 bool halSuccess = true; /* We set this to false to abort the operation. */
945 autoDBusError dbusError;
946
947 RTMemAutoPtr <DBusMessage, VBoxDBusMessageUnref> message, reply;
948 DBusMessageIter iterGet, iterProps;
949
950 /* Initialise the return array to NULLs */
951 for (size_t i = 0; i < cProps; ++i)
952 papszValues[i] = NULL;
953
954 /* Send a GetAllProperties message to hald */
955 message = dbus_message_new_method_call ("org.freedesktop.Hal", pszUdi,
956 "org.freedesktop.Hal.Device",
957 "GetAllProperties");
958 if (!message)
959 rc = VERR_NO_MEMORY;
960 if (halSuccess && RT_SUCCESS(rc))
961 {
962 reply = dbus_connection_send_with_reply_and_block (pConnection,
963 message.get(), -1,
964 &dbusError.get());
965 if (!reply)
966 halSuccess = false;
967 }
968
969 /* Parse the reply */
970 if (halSuccess && RT_SUCCESS(rc))
971 {
972 dbus_message_iter_init (reply.get(), &iterGet);
973 if ( dbus_message_iter_get_arg_type (&iterGet) != DBUS_TYPE_ARRAY
974 && dbus_message_iter_get_element_type (&iterGet) != DBUS_TYPE_DICT_ENTRY)
975 halSuccess = false;
976 }
977 if (halSuccess && RT_SUCCESS(rc))
978 dbus_message_iter_recurse (&iterGet, &iterProps);
979 /* Go through all entries in the reply and see if any match our keys. */
980 while ( halSuccess && RT_SUCCESS(rc)
981 && dbus_message_iter_get_arg_type (&iterProps)
982 == DBUS_TYPE_DICT_ENTRY)
983 {
984 const char *pszKey;
985 DBusMessageIter iterEntry, iterValue;
986 dbus_message_iter_recurse (&iterProps, &iterEntry);
987 dbus_message_iter_get_basic (&iterEntry, &pszKey);
988 dbus_message_iter_next (&iterEntry);
989 dbus_message_iter_recurse (&iterEntry, &iterValue);
990 /* Fill in any matches. */
991 for (size_t i = 0; i < cProps; ++i)
992 if (strcmp (pszKey, papszKeys[i]) == 0)
993 {
994 if (dbus_message_iter_get_arg_type (&iterValue) == DBUS_TYPE_STRING)
995 dbus_message_iter_get_basic (&iterValue, &papszValues[i]);
996 }
997 dbus_message_iter_next (&iterProps);
998 }
999 if (RT_SUCCESS(rc) && halSuccess)
1000 *pMessage = reply.release();
1001 if (dbusError.HasName (DBUS_ERROR_NO_MEMORY))
1002 rc = VERR_NO_MEMORY;
1003 LogFlowFunc (("rc=%Rrc, *pMessage.value()=%p\n", rc, (*pMessage).get()));
1004 dbusError.FlowLog();
1005 return rc;
1006}
1007
1008/**
1009 * Read a set of string properties for a device. If some properties do not
1010 * exist or are not of type DBUS_TYPE_STRING, we will still fetch the others.
1011 * @returns iprt status code. If the operation failed for non-fatal reasons
1012 * then we return success and set *pfSuccess to false.
1013 * @param pConnection an initialised connection DBus
1014 * @param pszUdi the Udi of the device
1015 * @param cProps the number of property values to look up
1016 * @param papszKeys the keys of the properties to be looked up
1017 * @param pMatches pointer to an empty array of iprt::MiniString to append the
1018 * results to. NOT optional.
1019 * @param pfMatches pointer to an array of boolean values indicating
1020 * whether the respective property is a string. If this
1021 * is not supplied then all properties must be strings
1022 * for the operation to be considered successful
1023 * @param pfSuccess will be set to true if the operation succeeds
1024 */
1025/* static */
1026int halGetPropertyStringsVector (DBusConnection *pConnection,
1027 const char *pszUdi, size_t cProps,
1028 const char **papszKeys,
1029 std::vector<iprt::MiniString> *pMatches,
1030 bool *pfMatches, bool *pfSuccess)
1031{
1032 AssertPtrReturn (pConnection, VERR_INVALID_POINTER);
1033 AssertPtrReturn (pszUdi, VERR_INVALID_POINTER);
1034 AssertPtrReturn (papszKeys, VERR_INVALID_POINTER);
1035 AssertPtrReturn (pMatches, VERR_INVALID_POINTER);
1036 AssertReturn((pfMatches == NULL) || VALID_PTR (pfMatches), VERR_INVALID_POINTER);
1037 AssertReturn((pfSuccess == NULL) || VALID_PTR (pfSuccess), VERR_INVALID_POINTER);
1038 AssertReturn(pMatches->empty(), VERR_INVALID_PARAMETER);
1039 LogFlowFunc (("pConnection=%p, pszUdi=%s, cProps=%llu, papszKeys=%p, pMatches=%p, pfMatches=%p, pfSuccess=%p\n",
1040 pConnection, pszUdi, cProps, papszKeys, pMatches, pfMatches, pfSuccess));
1041 RTMemAutoPtr <char *> values(cProps);
1042 RTMemAutoPtr <DBusMessage, VBoxDBusMessageUnref> message;
1043 bool halSuccess = true;
1044 int rc = halGetPropertyStrings (pConnection, pszUdi, cProps, papszKeys,
1045 values.get(), &message);
1046 if (!message)
1047 halSuccess = false;
1048 for (size_t i = 0; RT_SUCCESS(rc) && halSuccess && i < cProps; ++i)
1049 {
1050 bool fMatches = values[i] != NULL;
1051 if (pfMatches != NULL)
1052 pfMatches[i] = fMatches;
1053 else
1054 halSuccess = fMatches;
1055 try
1056 {
1057 pMatches->push_back(fMatches ? values[i] : "");
1058 }
1059 catch(std::bad_alloc &e)
1060 {
1061 rc = VERR_NO_MEMORY;
1062 }
1063 }
1064 if (pfSuccess != NULL)
1065 *pfSuccess = halSuccess;
1066 if (RT_SUCCESS(rc) && halSuccess)
1067 {
1068 Assert (pMatches->size() == cProps);
1069 AssertForEach (j, size_t, 0, cProps, (pfMatches == NULL)
1070 || (pfMatches[j] == true)
1071 || ((pfMatches[j] == false) && (pMatches[j].size() == 0)));
1072 }
1073 LogFlowFunc (("rc=%Rrc, halSuccess=%d\n", rc, halSuccess));
1074 return rc;
1075}
1076
1077/**
1078 * Send an SCSI INQUIRY command to a device and return selected information.
1079 * @returns iprt status code
1080 * @returns VERR_TRY_AGAIN if the query failed but might succeed next time
1081 * @param pszNode the full path to the device node
1082 * @param pu8Type where to store the SCSI device type on success (optional)
1083 * @param pchVendor where to store the vendor id string on success (optional)
1084 * @param cchVendor the size of the @a pchVendor buffer
1085 * @param pchModel where to store the product id string on success (optional)
1086 * @param cchModel the size of the @a pchModel buffer
1087 * @note check documentation on the SCSI INQUIRY command and the Linux kernel
1088 * SCSI headers included above if you want to understand what is going
1089 * on in this method.
1090 */
1091/* static */
1092int scsiDoInquiry(const char *pszNode, uint8_t *pu8Type, char *pchVendor,
1093 size_t cchVendor, char *pchModel, size_t cchModel)
1094{
1095 LogRelFlowFunc(("pszNode=%s, pu8Type=%p, pchVendor=%p, cchVendor=%llu, pchModel=%p, cchModel=%llu\n",
1096 pszNode, pu8Type, pchVendor, cchVendor, pchModel,
1097 cchModel));
1098 AssertPtrReturn(pszNode, VERR_INVALID_POINTER);
1099 AssertPtrNullReturn(pu8Type, VERR_INVALID_POINTER);
1100 AssertPtrNullReturn(pchVendor, VERR_INVALID_POINTER);
1101 AssertPtrNullReturn(pchModel, VERR_INVALID_POINTER);
1102
1103 sg_io_hdr_t ScsiIoReq = {0};
1104 unsigned char u8Response[96] = { 0 };
1105 unsigned char u8Command[6] =
1106 { INQUIRY, 0, 0, 0, sizeof(u8Response), 0 }; /* INQUIRY */
1107 int rc, rcIoCtl = 0;
1108 RTFILE file;
1109 rc = RTFileOpen(&file, pszNode, RTFILE_O_READ);
1110 if (RT_SUCCESS(rc))
1111 {
1112 ScsiIoReq.interface_id = 'S';
1113 ScsiIoReq.dxfer_direction = SG_DXFER_FROM_DEV;
1114 ScsiIoReq.cmd_len = sizeof(u8Command);
1115 ScsiIoReq.dxfer_len = sizeof(u8Response);
1116 ScsiIoReq.dxferp = u8Response;
1117 ScsiIoReq.cmdp = u8Command;
1118 ScsiIoReq.timeout = 5000 /* ms */;
1119 rc = RTFileIoCtl(file, SG_IO, &ScsiIoReq, 0, &rcIoCtl);
1120 if (RT_SUCCESS(rc) && rcIoCtl < 0)
1121 rc = VERR_NOT_SUPPORTED;
1122 RTFileClose(file);
1123 }
1124 if (RT_SUCCESS(rc))
1125 {
1126 if ( (ScsiIoReq.status >> 1 == GOOD)
1127 || (ScsiIoReq.status >> 1 == INTERMEDIATE_GOOD)
1128 || (ScsiIoReq.status >> 1 == INTERMEDIATE_C_GOOD)
1129 || (ScsiIoReq.status >> 1 == COMMAND_TERMINATED))
1130 {
1131 if (pu8Type)
1132 *pu8Type = u8Response[0] & 0x1f;
1133 if (pchVendor)
1134 RTStrPrintf(pchVendor, cchVendor, "%.8s",
1135 (char *) &u8Response[8] /* vendor id string */);
1136 if (pchModel)
1137 RTStrPrintf(pchModel, cchModel, "%.16s",
1138 (char *) &u8Response[16] /* product id string */);
1139 }
1140 else if ( ScsiIoReq.status >> 1 != BUSY
1141 && ScsiIoReq.status >> 1 != QUEUE_FULL
1142 && ScsiIoReq.status >> 1 != CHECK_CONDITION)
1143 rc = VERR_DEV_IO_ERROR; /* Actually, these should never happen */
1144 else
1145 rc = VERR_TRY_AGAIN;
1146 }
1147 LogRelFlowFunc(("returning %Rrc\n", rc));
1148 if (RT_SUCCESS(rc))
1149 LogRelFlowFunc((" type=%u, vendor=%.8s, product=%.16s\n",
1150 u8Response[0] & 0x1f, (char *) &u8Response[8],
1151 (char *) &u8Response[16]));
1152 return rc;
1153}
1154
1155class sysfsBlockDev
1156{
1157public:
1158 sysfsBlockDev(const char *pcszName, bool wantDVD)
1159 : mpcszName(pcszName), mwantDVD(wantDVD), misValid(false)
1160 {
1161 if (findDeviceNode())
1162 {
1163 if (mwantDVD)
1164 validateAndInitForDVD();
1165 else
1166 validateAndInitForFloppy();
1167 }
1168 }
1169private:
1170 /** The name of the subdirectory of /sys/block for this device */
1171 const char *mpcszName;
1172 /** Are we looking for a floppy or a DVD device? */
1173 bool mwantDVD;
1174 /** The device node for the device */
1175 char mszNode[RTPATH_MAX];
1176 /** Is this entry a valid specimen of what we are looking for? */
1177 bool misValid;
1178 /** Human readible drive description string */
1179 char mszDesc[256];
1180 /** Unique identifier for the drive. Should be identical to hal's UDI for
1181 * the device. May not be unique for two identical drives. */
1182 char mszUdi[256];
1183private:
1184 /* Private methods */
1185
1186 /**
1187 * Fill in the device node member based on the /sys/block subdirectory.
1188 * @returns boolean success value
1189 */
1190 bool findDeviceNode()
1191 {
1192 dev_t dev = RTLinuxSysFsReadDevNumFile("block/%s/dev", mpcszName);
1193 if (dev == 0)
1194 return false;
1195 if (RTLinuxFindDevicePath(dev, RTFS_TYPE_DEV_BLOCK, mszNode,
1196 sizeof(mszNode), "%s", mpcszName) < 0)
1197 return false;
1198 return true;
1199 }
1200
1201 /** Check whether the sysfs block entry is valid for a DVD device and
1202 * initialise the string data members for the object. We try to get all
1203 * the information we need from sysfs if possible, to avoid unnecessarily
1204 * poking the device, and if that fails we fall back to an SCSI INQUIRY
1205 * command. */
1206 void validateAndInitForDVD()
1207 {
1208 char szVendor[128], szModel[128];
1209 ssize_t cchVendor, cchModel;
1210 int64_t type = RTLinuxSysFsReadIntFile(10, "block/%s/device/type",
1211 mpcszName);
1212 if (type >= 0 && type != TYPE_ROM)
1213 return;
1214 if (type == 5)
1215 {
1216 cchVendor = RTLinuxSysFsReadStrFile(szVendor, sizeof(szVendor),
1217 "block/%s/device/vendor",
1218 mpcszName);
1219 if (cchVendor >= 0)
1220 {
1221 cchModel = RTLinuxSysFsReadStrFile(szModel, sizeof(szModel),
1222 "block/%s/device/model",
1223 mpcszName);
1224 if (cchModel >= 0)
1225 {
1226 misValid = true;
1227 setDeviceStrings(szVendor, szModel);
1228 return;
1229 }
1230 }
1231 }
1232 probeAndInitForDVD();
1233 }
1234
1235 /** Try to find out whether a device is a DVD drive by sending it an
1236 * SCSI INQUIRY command. If it is, initialise the string and validity
1237 * data members for the object based on the returned data.
1238 */
1239 void probeAndInitForDVD()
1240 {
1241 AssertReturnVoid(mszNode[0] != '\0');
1242 uint8_t u8Type = 0;
1243 char szVendor[128] = "";
1244 char szModel[128] = "";
1245 for (unsigned i = 0; i < 5; ++i) /* Give the device five chances */
1246 {
1247 int rc = scsiDoInquiry(mszNode, &u8Type, szVendor,
1248 sizeof(szVendor), szModel,
1249 sizeof(szModel));
1250 if (RT_SUCCESS(rc))
1251 {
1252 if (u8Type != TYPE_ROM)
1253 return;
1254 misValid = true;
1255 setDeviceStrings(szVendor, szModel);
1256 return;
1257 }
1258 if (rc != VERR_TRY_AGAIN)
1259 return;
1260 RTThreadSleep(100); /* wait a little before retrying */
1261 }
1262 }
1263
1264 /**
1265 * Initialise the object device strings (description and UDI) based on
1266 * vendor and model name strings.
1267 * @param pszVendor the vendor ID string
1268 * @param pszModel the product ID string
1269 */
1270 void setDeviceStrings(const char *pszVendor, const char *pszModel)
1271 {
1272 char szCleaned[128];
1273 size_t cchVendor = strLenStripped(pszVendor);
1274 size_t cchModel = strLenStripped(pszModel);
1275
1276 /* Create a cleaned version of the model string for the UDI string. */
1277 for (unsigned i = 0; pszModel[i] != '\0' && i < sizeof(szCleaned); ++i)
1278 if ( (pszModel[i] >= '0' && pszModel[i] <= '9')
1279 || (pszModel[i] >= 'A' && pszModel[i] <= 'z'))
1280 szCleaned[i] = pszModel[i];
1281 else
1282 szCleaned[i] = '_';
1283 szCleaned[RT_MIN(cchModel, sizeof(szCleaned) - 1)] = '\0';
1284
1285 /* Construct the description string as "Vendor Product" */
1286 if (cchVendor > 0)
1287 RTStrPrintf(mszDesc, sizeof(mszDesc), "%.*s %s", cchVendor,
1288 pszVendor,
1289 cchModel > 0 ? pszModel : "(unknown drive model)");
1290 else
1291 RTStrPrintf(mszDesc, sizeof(mszDesc), "%s", pszModel);
1292 /* Construct the UDI string */
1293 if (cchModel)
1294 RTStrPrintf(mszUdi, sizeof(mszUdi),
1295 "/org/freedesktop/Hal/devices/storage_model_%s",
1296 szCleaned);
1297 else
1298 mszUdi[0] = '\0';
1299 }
1300
1301 /** Check whether the sysfs block entry is valid for a floppy device and
1302 * initialise the string data members for the object. Since we only
1303 * support floppies using the basic "floppy" driver, we just check the
1304 * entry name and the bus type ("platform"). */
1305 void validateAndInitForFloppy()
1306 {
1307 floppy_drive_name szName;
1308 int rcIoCtl;
1309 if ( mpcszName[0] != 'f'
1310 || mpcszName[1] != 'd'
1311 || mpcszName[2] < '0'
1312 || mpcszName[2] > '3'
1313 || mpcszName[3] != '\0')
1314 return;
1315 RTFILE file;
1316 int rc = RTFileOpen(&file, mszNode, RTFILE_O_READ);
1317 /** @note the next line can produce a warning, as the ioctl request
1318 * field is defined as signed, but the Linux ioctl definition macros
1319 * produce unsigned constants. */
1320 rc = RTFileIoCtl(file, FDGETDRVTYP, szName, 0, &rcIoCtl);
1321 RTFileClose(file);
1322 if (rcIoCtl < 0)
1323 return;
1324 misValid = true;
1325 strcpy(mszDesc, (mpcszName[2] == '0') ? "PC Floppy drive"
1326 : (mpcszName[2] == '1') ? "Second PC Floppy drive"
1327 : (mpcszName[2] == '2') ? "Third PC Floppy drive"
1328 : "Fourth PC Floppy drive");
1329 RTStrPrintf(mszUdi, sizeof(mszUdi),
1330 "/org/freedesktop/Hal/devices/platform_floppy_%u_storage",
1331 mpcszName[2]);
1332 }
1333
1334public:
1335 bool isValid()
1336 {
1337 return misValid;
1338 }
1339 const char *getDesc()
1340 {
1341 return mszDesc;
1342 }
1343 const char *getUdi()
1344 {
1345 return mszUdi;
1346 }
1347 const char *getNode()
1348 {
1349 return mszNode;
1350 }
1351};
1352
1353/**
1354 * Helper function to query the sysfs subsystem for information about DVD
1355 * drives attached to the system.
1356 * @returns iprt status code
1357 * @param pList where to add information about the drives detected
1358 * @param isDVD are we looking for DVDs or floppies?
1359 * @param pfSuccess Did we find anything?
1360 *
1361 * @returns IPRT status code
1362 */
1363/* static */
1364int getDriveInfoFromSysfs(DriveInfoList *pList, bool isDVD, bool *pfSuccess)
1365{
1366 AssertPtrReturn(pList, VERR_INVALID_POINTER);
1367 AssertPtrNullReturn(pfSuccess, VERR_INVALID_POINTER); /* Valid or Null */
1368 LogFlowFunc (("pList=%p, isDVD=%u, pfSuccess=%p\n",
1369 pList, (unsigned) isDVD, pfSuccess));
1370 PRTDIR pDir = NULL;
1371 RTDIRENTRY entry = {0};
1372 int rc;
1373 bool fSuccess;
1374 unsigned cFound = 0;
1375
1376 rc = RTDirOpen(&pDir, "/sys/block");
1377 if (RT_SUCCESS(rc))
1378 while (true)
1379 {
1380 rc = RTDirRead(pDir, &entry, NULL);
1381 Assert(rc != VERR_BUFFER_OVERFLOW); /* Should never happen... */
1382 if (RT_FAILURE(rc)) /* Including overflow and no more files */
1383 break;
1384 if (entry.szName[0] == '.')
1385 continue;
1386 sysfsBlockDev dev(entry.szName, isDVD);
1387 if (!dev.isValid())
1388 continue;
1389 try
1390 {
1391 pList->push_back(DriveInfo(dev.getNode(), dev.getUdi(),
1392 dev.getDesc()));
1393 }
1394 catch(std::bad_alloc &e)
1395 {
1396 rc = VERR_NO_MEMORY;
1397 break;
1398 }
1399 ++cFound;
1400 }
1401 RTDirClose(pDir);
1402 if (rc == VERR_NO_MORE_FILES)
1403 rc = VINF_SUCCESS;
1404 fSuccess = (RT_SUCCESS(rc) && cFound > 0);
1405 if (!fSuccess)
1406 /* Clean up again */
1407 for (unsigned i = 0; i < cFound; ++i)
1408 pList->pop_back();
1409 if (pfSuccess)
1410 *pfSuccess = fSuccess;
1411 LogFlow (("rc=%Rrc, fSuccess=%u\n", rc, (unsigned) fSuccess));
1412 return rc;
1413}
1414
1415/**
1416 * Helper function to query the hal subsystem for information about drives
1417 * attached to the system.
1418 * @returns iprt status code
1419 * @param pList where to add information about the drives detected
1420 * @param isDVD are we looking for DVDs or floppies?
1421 * @param pfSuccess will be set to true if all interactions with hal
1422 * succeeded and to false otherwise. Optional.
1423 *
1424 * @returns IPRT status code
1425 */
1426/* static */
1427int getDriveInfoFromHal(DriveInfoList *pList, bool isDVD, bool *pfSuccess)
1428{
1429 AssertReturn(VALID_PTR (pList) && (pfSuccess == NULL || VALID_PTR (pfSuccess)),
1430 VERR_INVALID_POINTER);
1431 LogFlowFunc (("pList=%p, isDVD=%d, pfSuccess=%p\n", pList, isDVD, pfSuccess));
1432 int rc = VINF_SUCCESS; /* We set this to failure on fatal errors. */
1433 bool halSuccess = true; /* We set this to false to abort the operation. */
1434 autoDBusError dbusError;
1435
1436 RTMemAutoPtr <DBusMessage, VBoxDBusMessageUnref> message, replyFind, replyGet;
1437 RTMemAutoPtr <DBusConnection, VBoxHalShutdown> dbusConnection;
1438 DBusMessageIter iterFind, iterUdis;
1439
1440 try
1441 {
1442 rc = halInit (&dbusConnection);
1443 if (!dbusConnection)
1444 halSuccess = false;
1445 if (halSuccess && RT_SUCCESS(rc))
1446 {
1447 rc = halFindDeviceStringMatch (dbusConnection.get(), "storage.drive_type",
1448 isDVD ? "cdrom" : "floppy", &replyFind);
1449 if (!replyFind)
1450 halSuccess = false;
1451 }
1452 if (halSuccess && RT_SUCCESS(rc))
1453 {
1454 dbus_message_iter_init (replyFind.get(), &iterFind);
1455 if (dbus_message_iter_get_arg_type (&iterFind) != DBUS_TYPE_ARRAY)
1456 halSuccess = false;
1457 }
1458 if (halSuccess && RT_SUCCESS(rc))
1459 dbus_message_iter_recurse (&iterFind, &iterUdis);
1460 for (; halSuccess && RT_SUCCESS(rc)
1461 && dbus_message_iter_get_arg_type (&iterUdis) == DBUS_TYPE_STRING;
1462 dbus_message_iter_next(&iterUdis))
1463 {
1464 /* Now get all properties from the iterator */
1465 const char *pszUdi;
1466 dbus_message_iter_get_basic (&iterUdis, &pszUdi);
1467 static const char *papszKeys[] =
1468 { "block.device", "info.product", "info.vendor" };
1469 char *papszValues[RT_ELEMENTS (papszKeys)];
1470 rc = halGetPropertyStrings (dbusConnection.get(), pszUdi, RT_ELEMENTS (papszKeys),
1471 papszKeys, papszValues, &replyGet);
1472 iprt::MiniString description;
1473 const char *pszDevice = papszValues[0], *pszProduct = papszValues[1],
1474 *pszVendor = papszValues[2];
1475 if (!!replyGet && pszDevice == NULL)
1476 halSuccess = false;
1477 if (!!replyGet && pszDevice != NULL)
1478 {
1479 if ((pszVendor != NULL) && (pszVendor[0] != '\0'))
1480 {
1481 description.append(pszVendor);
1482 description.append(" ");
1483 }
1484 if ((pszProduct != NULL && pszProduct[0] != '\0'))
1485 description.append(pszProduct);
1486 pList->push_back (DriveInfo (pszDevice, pszUdi, description));
1487 }
1488 }
1489 if (dbusError.HasName (DBUS_ERROR_NO_MEMORY))
1490 rc = VERR_NO_MEMORY;
1491 /* If we found nothing something may have gone wrong with hal, so
1492 * report failure to fall back to other methods. */
1493 if (pList->size() == 0)
1494 halSuccess = false;
1495 if (pfSuccess != NULL)
1496 *pfSuccess = halSuccess;
1497 }
1498 catch(std::bad_alloc &e)
1499 {
1500 rc = VERR_NO_MEMORY;
1501 }
1502 LogFlow (("rc=%Rrc, halSuccess=%d\n", rc, halSuccess));
1503 dbusError.FlowLog();
1504 return rc;
1505}
1506
1507/**
1508 * Helper function to query the hal subsystem for information about USB devices
1509 * attached to the system.
1510 * @returns iprt status code
1511 * @param pList where to add information about the devices detected
1512 * @param pfSuccess will be set to true if all interactions with hal
1513 * succeeded and to false otherwise. Optional.
1514 *
1515 * @returns IPRT status code
1516 */
1517/* static */
1518int getUSBDeviceInfoFromHal(USBDeviceInfoList *pList, bool *pfSuccess)
1519{
1520 AssertReturn(VALID_PTR (pList) && (pfSuccess == NULL || VALID_PTR (pfSuccess)),
1521 VERR_INVALID_POINTER);
1522 LogFlowFunc (("pList=%p, pfSuccess=%p\n", pList, pfSuccess));
1523 int rc = VINF_SUCCESS; /* We set this to failure on fatal errors. */
1524 bool halSuccess = true; /* We set this to false to abort the operation. */
1525 autoDBusError dbusError;
1526
1527 RTMemAutoPtr <DBusMessage, VBoxDBusMessageUnref> message, replyFind, replyGet;
1528 RTMemAutoPtr <DBusConnection, VBoxHalShutdown> dbusConnection;
1529 DBusMessageIter iterFind, iterUdis;
1530
1531 /* Connect to hal */
1532 rc = halInit (&dbusConnection);
1533 if (!dbusConnection)
1534 halSuccess = false;
1535 /* Get an array of all devices in the usb_device subsystem */
1536 if (halSuccess && RT_SUCCESS(rc))
1537 {
1538 rc = halFindDeviceStringMatch (dbusConnection.get(), "info.subsystem",
1539 "usb_device", &replyFind);
1540 if (!replyFind)
1541 halSuccess = false;
1542 }
1543 if (halSuccess && RT_SUCCESS(rc))
1544 {
1545 dbus_message_iter_init (replyFind.get(), &iterFind);
1546 if (dbus_message_iter_get_arg_type (&iterFind) != DBUS_TYPE_ARRAY)
1547 halSuccess = false;
1548 }
1549 /* Recurse down into the array and query interesting information about the
1550 * entries. */
1551 if (halSuccess && RT_SUCCESS(rc))
1552 dbus_message_iter_recurse (&iterFind, &iterUdis);
1553 for (; halSuccess && RT_SUCCESS(rc)
1554 && dbus_message_iter_get_arg_type (&iterUdis) == DBUS_TYPE_STRING;
1555 dbus_message_iter_next(&iterUdis))
1556 {
1557 /* Get the device node and the sysfs path for the current entry. */
1558 const char *pszUdi;
1559 dbus_message_iter_get_basic (&iterUdis, &pszUdi);
1560 static const char *papszKeys[] = { "linux.device_file", "linux.sysfs_path" };
1561 char *papszValues[RT_ELEMENTS (papszKeys)];
1562 rc = halGetPropertyStrings (dbusConnection.get(), pszUdi, RT_ELEMENTS (papszKeys),
1563 papszKeys, papszValues, &replyGet);
1564 const char *pszDevice = papszValues[0], *pszSysfsPath = papszValues[1];
1565 /* Get the interfaces. */
1566 if (!!replyGet && pszDevice && pszSysfsPath)
1567 {
1568 USBDeviceInfo info (pszDevice, pszSysfsPath);
1569 bool ifaceSuccess = true; /* If we can't get the interfaces, just
1570 * skip this one device. */
1571 rc = getUSBInterfacesFromHal (&info.mInterfaces, pszUdi, &ifaceSuccess);
1572 if (RT_SUCCESS(rc) && halSuccess && ifaceSuccess)
1573 try
1574 {
1575 pList->push_back (info);
1576 }
1577 catch(std::bad_alloc &e)
1578 {
1579 rc = VERR_NO_MEMORY;
1580 }
1581 }
1582 }
1583 if (dbusError.HasName (DBUS_ERROR_NO_MEMORY))
1584 rc = VERR_NO_MEMORY;
1585 if (pfSuccess != NULL)
1586 *pfSuccess = halSuccess;
1587 LogFlow (("rc=%Rrc, halSuccess=%d\n", rc, halSuccess));
1588 dbusError.FlowLog();
1589 return rc;
1590}
1591
1592/**
1593 * Helper function to query the hal subsystem for information about USB devices
1594 * attached to the system, using the older API.
1595 * @returns iprt status code
1596 * @param pList where to add information about the devices detected
1597 * @param pfSuccess will be set to true if all interactions with hal
1598 * succeeded and to false otherwise. Optional.
1599 *
1600 * @returns IPRT status code
1601 */
1602/* static */
1603int getOldUSBDeviceInfoFromHal(USBDeviceInfoList *pList, bool *pfSuccess)
1604{
1605 AssertReturn(VALID_PTR (pList) && (pfSuccess == NULL || VALID_PTR (pfSuccess)),
1606 VERR_INVALID_POINTER);
1607 LogFlowFunc (("pList=%p, pfSuccess=%p\n", pList, pfSuccess));
1608 int rc = VINF_SUCCESS; /* We set this to failure on fatal errors. */
1609 bool halSuccess = true; /* We set this to false to abort the operation. */
1610 autoDBusError dbusError;
1611
1612 RTMemAutoPtr <DBusMessage, VBoxDBusMessageUnref> message, replyFind, replyGet;
1613 RTMemAutoPtr <DBusConnection, VBoxHalShutdown> dbusConnection;
1614 DBusMessageIter iterFind, iterUdis;
1615
1616 /* Connect to hal */
1617 rc = halInit (&dbusConnection);
1618 if (!dbusConnection)
1619 halSuccess = false;
1620 /* Get an array of all devices in the usb_device subsystem */
1621 if (halSuccess && RT_SUCCESS(rc))
1622 {
1623 rc = halFindDeviceStringMatch (dbusConnection.get(), "info.category",
1624 "usbraw", &replyFind);
1625 if (!replyFind)
1626 halSuccess = false;
1627 }
1628 if (halSuccess && RT_SUCCESS(rc))
1629 {
1630 dbus_message_iter_init (replyFind.get(), &iterFind);
1631 if (dbus_message_iter_get_arg_type (&iterFind) != DBUS_TYPE_ARRAY)
1632 halSuccess = false;
1633 }
1634 /* Recurse down into the array and query interesting information about the
1635 * entries. */
1636 if (halSuccess && RT_SUCCESS(rc))
1637 dbus_message_iter_recurse (&iterFind, &iterUdis);
1638 for (; halSuccess && RT_SUCCESS(rc)
1639 && dbus_message_iter_get_arg_type (&iterUdis) == DBUS_TYPE_STRING;
1640 dbus_message_iter_next(&iterUdis))
1641 {
1642 /* Get the device node and the sysfs path for the current entry. */
1643 const char *pszUdi;
1644 dbus_message_iter_get_basic (&iterUdis, &pszUdi);
1645 static const char *papszKeys[] = { "linux.device_file", "info.parent" };
1646 char *papszValues[RT_ELEMENTS (papszKeys)];
1647 rc = halGetPropertyStrings (dbusConnection.get(), pszUdi, RT_ELEMENTS (papszKeys),
1648 papszKeys, papszValues, &replyGet);
1649 const char *pszDevice = papszValues[0], *pszSysfsPath = papszValues[1];
1650 /* Get the interfaces. */
1651 if (!!replyGet && pszDevice && pszSysfsPath)
1652 {
1653 USBDeviceInfo info (pszDevice, pszSysfsPath);
1654 bool ifaceSuccess = false; /* If we can't get the interfaces, just
1655 * skip this one device. */
1656 rc = getUSBInterfacesFromHal (&info.mInterfaces, pszSysfsPath,
1657 &ifaceSuccess);
1658 if (RT_SUCCESS(rc) && halSuccess && ifaceSuccess)
1659 try
1660 {
1661 pList->push_back (info);
1662 }
1663 catch(std::bad_alloc &e)
1664 {
1665 rc = VERR_NO_MEMORY;
1666 }
1667 }
1668 }
1669 if (dbusError.HasName (DBUS_ERROR_NO_MEMORY))
1670 rc = VERR_NO_MEMORY;
1671 if (pfSuccess != NULL)
1672 *pfSuccess = halSuccess;
1673 LogFlow (("rc=%Rrc, halSuccess=%d\n", rc, halSuccess));
1674 dbusError.FlowLog();
1675 return rc;
1676}
1677
1678/**
1679 * Helper function to query the hal subsystem for information about USB devices
1680 * attached to the system.
1681 * @returns iprt status code
1682 * @param pList where to add information about the devices detected. If
1683 * certain interfaces are not found (@a pfFound is false on
1684 * return) this may contain invalid information.
1685 * @param pcszUdi the hal UDI of the device
1686 * @param pfSuccess will be set to true if the operation succeeds and to
1687 * false if it fails for non-critical reasons. Optional.
1688 *
1689 * @returns IPRT status code
1690 */
1691/* static */
1692int getUSBInterfacesFromHal(std::vector<iprt::MiniString> *pList,
1693 const char *pcszUdi, bool *pfSuccess)
1694{
1695 AssertReturn(VALID_PTR (pList) && VALID_PTR (pcszUdi) &&
1696 (pfSuccess == NULL || VALID_PTR (pfSuccess)),
1697 VERR_INVALID_POINTER);
1698 LogFlowFunc (("pList=%p, pcszUdi=%s, pfSuccess=%p\n", pList, pcszUdi,
1699 pfSuccess));
1700 int rc = VINF_SUCCESS; /* We set this to failure on fatal errors. */
1701 bool halSuccess = true; /* We set this to false to abort the operation. */
1702 autoDBusError dbusError;
1703
1704 RTMemAutoPtr <DBusMessage, VBoxDBusMessageUnref> message, replyFind, replyGet;
1705 RTMemAutoPtr <DBusConnection, VBoxHalShutdown> dbusConnection;
1706 DBusMessageIter iterFind, iterUdis;
1707
1708 rc = halInit (&dbusConnection);
1709 if (!dbusConnection)
1710 halSuccess = false;
1711 if (halSuccess && RT_SUCCESS(rc))
1712 {
1713 /* Look for children of the current UDI. */
1714 rc = halFindDeviceStringMatch (dbusConnection.get(), "info.parent",
1715 pcszUdi, &replyFind);
1716 if (!replyFind)
1717 halSuccess = false;
1718 }
1719 if (halSuccess && RT_SUCCESS(rc))
1720 {
1721 dbus_message_iter_init (replyFind.get(), &iterFind);
1722 if (dbus_message_iter_get_arg_type (&iterFind) != DBUS_TYPE_ARRAY)
1723 halSuccess = false;
1724 }
1725 if (halSuccess && RT_SUCCESS(rc))
1726 dbus_message_iter_recurse (&iterFind, &iterUdis);
1727 for (; halSuccess && RT_SUCCESS(rc)
1728 && dbus_message_iter_get_arg_type (&iterUdis) == DBUS_TYPE_STRING;
1729 dbus_message_iter_next(&iterUdis))
1730 {
1731 /* Now get the sysfs path and the subsystem from the iterator */
1732 const char *pszUdi;
1733 dbus_message_iter_get_basic (&iterUdis, &pszUdi);
1734 static const char *papszKeys[] = { "linux.sysfs_path", "info.subsystem",
1735 "linux.subsystem" };
1736 char *papszValues[RT_ELEMENTS (papszKeys)];
1737 rc = halGetPropertyStrings (dbusConnection.get(), pszUdi, RT_ELEMENTS (papszKeys),
1738 papszKeys, papszValues, &replyGet);
1739 const char *pszSysfsPath = papszValues[0], *pszInfoSubsystem = papszValues[1],
1740 *pszLinuxSubsystem = papszValues[2];
1741 if (!replyGet)
1742 halSuccess = false;
1743 if (!!replyGet && pszSysfsPath == NULL)
1744 halSuccess = false;
1745 if ( halSuccess && RT_SUCCESS(rc)
1746 && RTStrCmp (pszInfoSubsystem, "usb_device") != 0 /* Children of buses can also be devices. */
1747 && RTStrCmp (pszLinuxSubsystem, "usb_device") != 0)
1748 try
1749 {
1750 pList->push_back (pszSysfsPath);
1751 }
1752 catch(std::bad_alloc &e)
1753 {
1754 rc = VERR_NO_MEMORY;
1755 }
1756 }
1757 if (dbusError.HasName (DBUS_ERROR_NO_MEMORY))
1758 rc = VERR_NO_MEMORY;
1759 if (pfSuccess != NULL)
1760 *pfSuccess = halSuccess;
1761 LogFlow (("rc=%Rrc, halSuccess=%d\n", rc, halSuccess));
1762 dbusError.FlowLog();
1763 return rc;
1764}
1765
1766/**
1767 * When it is registered with DBus, this function will be called by
1768 * dbus_connection_read_write_dispatch each time a message is received over the
1769 * DBus connection. We check whether that message was caused by a hal device
1770 * hotplug event, and if so we set a flag. dbus_connection_read_write_dispatch
1771 * will return after calling its filter functions, and its caller should then
1772 * check the status of the flag passed to the filter function.
1773 *
1774 * @param pConnection The DBus connection we are using.
1775 * @param pMessage The DBus message which just arrived.
1776 * @param pvUser A pointer to the flag variable we are to set.
1777 */
1778/* static */
1779DBusHandlerResult dbusFilterFunction (DBusConnection * /* pConnection */,
1780 DBusMessage *pMessage, void *pvUser)
1781{
1782 volatile bool *pTriggered = reinterpret_cast<volatile bool *> (pvUser);
1783 if ( dbus_message_is_signal (pMessage, "org.freedesktop.Hal.Manager",
1784 "DeviceAdded")
1785 || dbus_message_is_signal (pMessage, "org.freedesktop.Hal.Manager",
1786 "DeviceRemoved"))
1787 {
1788 *pTriggered = true;
1789 }
1790 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1791}
1792#endif /* RT_OS_LINUX && VBOX_WITH_DBUS */
1793
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