VirtualBox

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

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

Main/HostHardwareLinux: use sysfs for querying host drives if hal is not available

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