VirtualBox

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

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

Main: added USB to HostHardwareLinux.cpp

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 38.3 KB
Line 
1/* $Id: HostHardwareLinux.cpp 15401 2008-12-12 22:05:34Z vboxsync $ */
2/** @file
3 * Classes for handling hardware detection under Linux. Please feel free to
4 * expand these to work for other systems (Solaris!) or to add new ones for
5 * other systems.
6 */
7
8/*
9 * Copyright (C) 2008 Sun Microsystems, Inc.
10 *
11 * This file is part of VirtualBox Open Source Edition (OSE), as
12 * available from http://www.virtualbox.org. This file is free software;
13 * you can redistribute it and/or modify it under the terms of the GNU
14 * General Public License (GPL) as published by the Free Software
15 * Foundation, in version 2 as it comes in the "COPYING" file of the
16 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
17 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
18 *
19 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
20 * Clara, CA 95054 USA or visit http://www.sun.com if you need
21 * additional information or have any questions.
22 */
23
24#define LOG_GROUP LOG_GROUP_MAIN
25
26/*******************************************************************************
27* Header Files *
28*******************************************************************************/
29
30#include <HostHardwareLinux.h>
31
32#include <VBox/log.h>
33
34#include <iprt/env.h>
35#include <iprt/mem.h>
36#include <iprt/string.h>
37
38#ifdef RT_OS_LINUX
39# include <sys/types.h>
40# include <sys/stat.h>
41# include <unistd.h>
42# include <sys/ioctl.h>
43# include <fcntl.h>
44# include <mntent.h>
45/* bird: This is a hack to work around conflicts between these linux kernel headers
46 * and the GLIBC tcpip headers. They have different declarations of the 4
47 * standard byte order functions. */
48// # define _LINUX_BYTEORDER_GENERIC_H
49# include <linux/cdrom.h>
50# ifdef VBOX_WITH_DBUS
51# include <vbox-dbus.h>
52# endif
53# include <errno.h>
54#endif /* RT_OS_LINUX */
55
56/*******************************************************************************
57* Global Variables *
58*******************************************************************************/
59
60bool g_testHostHardwareLinux = false;
61static bool testing () { return g_testHostHardwareLinux; }
62
63/*******************************************************************************
64* Typedefs and Defines *
65*******************************************************************************/
66
67enum { DBUS_POLL_TIMEOUT = 2000 /* ms */ };
68
69
70static bool validateDevice(const char *deviceNode, bool isDVD);
71static int getDriveInfoFromEnv(const char *pszVar, DriveInfoList *pList,
72 bool isDVD, bool *pfSuccess);
73static int getDVDInfoFromMTab(char *mountTable, DriveInfoList *pList);
74#ifdef VBOX_WITH_DBUS
75/* This must be extern to be used in the RTMemAutoPtr template */
76extern void halShutdown (DBusConnection *pConnection);
77
78static int halInit(RTMemAutoPtr <DBusConnection, halShutdown> *pConnection);
79static int halFindDeviceStringMatch (DBusConnection *pConnection,
80 const char *pszKey, const char *pszValue,
81 RTMemAutoPtr <DBusMessage, VBoxDBusMessageUnref> *pMessage);
82static int halGetPropertyStrings (DBusConnection *pConnection,
83 const char *pszUdi, size_t cKeys,
84 const char **papszKeys, char **papszValues,
85 RTMemAutoPtr <DBusMessage, VBoxDBusMessageUnref> *pMessage);
86static int getDriveInfoFromHal(DriveInfoList *pList, bool isDVD,
87 bool *pfSuccess);
88static int getUSBDeviceInfoFromHal(USBDeviceInfoList *pList, bool *pfSuccess);
89static int getUSBInterfacesFromHal(std::vector <std::string> *pList,
90 const char *pcszUdi, bool *pfSuccess);
91static DBusHandlerResult dbusFilterFunction (DBusConnection *pConnection,
92 DBusMessage *pMessage, void *pvUser);
93#endif /* VBOX_WITH_DBUS */
94
95/**
96 * Updates the list of host DVD drives.
97 *
98 * @returns iprt status code
99 */
100int VBoxMainDriveInfo::updateDVDs ()
101{
102 LogFlowThisFunc (("entered\n"));
103 int rc = VINF_SUCCESS;
104 bool success = false; /* Have we succeeded in finding anything yet? */
105 try
106 {
107 mDVDList.clear ();
108#if defined(RT_OS_LINUX)
109#ifdef VBOX_WITH_DBUS
110 if (RT_SUCCESS (rc) && VBoxDBusCheckPresence() && (!success || testing()))
111 rc = getDriveInfoFromHal(&mDVDList, true /* isDVD */, &success);
112#endif /* VBOX_WITH_DBUS defined */
113 // On Linux without hal, the situation is much more complex. We will take a
114 // heuristical approach and also allow the user to specify a list of host
115 // CDROMs using an environment variable.
116 // The general strategy is to try some known device names and see of they
117 // exist. At last, we'll enumerate the /etc/fstab file (luckily there's an
118 // API to parse it) for CDROM devices. Ok, let's start!
119 if (RT_SUCCESS (rc) && (!success || testing()))
120 rc = getDriveInfoFromEnv ("VBOX_CDROM", &mDVDList, true /* isDVD */,
121 &success);
122 if (RT_SUCCESS (rc) && (!success || testing()))
123 {
124 // this is a good guess usually
125 if (validateDevice("/dev/cdrom", true))
126 mDVDList.push_back (DriveInfo ("/dev/cdrom"));
127
128 // check the mounted drives
129 rc = getDVDInfoFromMTab((char*)"/etc/mtab", &mDVDList);
130
131 // check the drives that can be mounted
132 if (RT_SUCCESS (rc))
133 rc = getDVDInfoFromMTab((char*)"/etc/fstab", &mDVDList);
134 }
135#endif
136 }
137 catch (std::bad_alloc)
138 {
139 rc = VERR_NO_MEMORY;
140 }
141 LogFlowThisFunc (("rc=%Rrc\n", rc));
142 return rc;
143}
144
145/**
146 * Updates the list of host floppy drives.
147 *
148 * @returns iprt status code
149 */
150int VBoxMainDriveInfo::updateFloppies ()
151{
152 LogFlowThisFunc (("entered\n"));
153 int rc = VINF_SUCCESS;
154 bool success = false; /* Have we succeeded in finding anything yet? */
155 try
156 {
157 mFloppyList.clear ();
158#if defined(RT_OS_LINUX)
159#ifdef VBOX_WITH_DBUS
160 if (RT_SUCCESS (rc) && VBoxDBusCheckPresence() && (!success || testing()))
161 rc = getDriveInfoFromHal(&mFloppyList, false /* isDVD */, &success);
162#endif /* VBOX_WITH_DBUS defined */
163 // As with the CDROMs, on Linux we have to take a multi-level approach
164 // involving parsing the mount tables. As this is not bulletproof, we'll
165 // give the user the chance to override the detection by an environment
166 // variable and skip the detection.
167 if (RT_SUCCESS (rc) && (!success || testing()))
168 rc = getDriveInfoFromEnv ("VBOX_FLOPPY", &mFloppyList, false /* isDVD */,
169 &success);
170
171 if (RT_SUCCESS (rc) && (!success || testing()))
172 {
173 // we assume that a floppy is always /dev/fd[x] with x from 0 to 7
174 char devName[10];
175 for (int i = 0; i <= 7; i++)
176 {
177 sprintf(devName, "/dev/fd%d", i);
178 if (validateDevice(devName, false))
179 mFloppyList.push_back (DriveInfo (devName));
180 }
181 }
182#endif
183 }
184 catch (std::bad_alloc)
185 {
186 rc = VERR_NO_MEMORY;
187 }
188 LogFlowThisFunc (("rc=%Rrc\n", rc));
189 return rc;
190}
191
192/**
193 * Updates the list of host USB devices.
194 *
195 * @returns iprt status code
196 */
197int VBoxMainUSBDeviceInfo::UpdateDevices ()
198{
199 LogFlowThisFunc (("entered\n"));
200 int rc = VINF_SUCCESS;
201 bool success = false; /* Have we succeeded in finding anything yet? */
202 try
203 {
204 mDeviceList.clear();
205#if defined(RT_OS_LINUX)
206#ifdef VBOX_WITH_DBUS
207 if (RT_SUCCESS (rc) && VBoxDBusCheckPresence() && (!success || testing()))
208 rc = getUSBDeviceInfoFromHal(&mDeviceList, &success);
209#endif /* VBOX_WITH_DBUS defined */
210#endif /* RT_OS_LINUX */
211 }
212 catch (std::bad_alloc)
213 {
214 rc = VERR_NO_MEMORY;
215 }
216 LogFlowThisFunc (("rc=%Rrc\n", rc));
217 return rc;
218}
219
220struct VBoxMainHotplugWaiter::Context
221{
222#if defined RT_OS_LINUX && defined VBOX_WITH_DBUS
223 /** The connection to DBus */
224 RTMemAutoPtr <DBusConnection, halShutdown> mConnection;
225 /** Semaphore which is set when a device is hotplugged and reset when
226 * it is read. */
227 bool mTriggered;
228 /** A flag to say that we wish to interrupt the current wait. */
229 bool mInterrupt;
230#endif /* defined RT_OS_LINUX && defined VBOX_WITH_DBUS */
231};
232
233/** Constructor */
234VBoxMainHotplugWaiter::VBoxMainHotplugWaiter ()
235{
236#if defined RT_OS_LINUX && defined VBOX_WITH_DBUS
237 int rc = VINF_SUCCESS;
238
239 mContext = new Context;
240 for (unsigned i = 0; RT_SUCCESS(rc) && i < 5 && !mContext->mConnection; ++i)
241 {
242 rc = halInit (&mContext->mConnection);
243 }
244 if (!mContext->mConnection)
245 rc = VERR_NOT_SUPPORTED;
246 if ( RT_SUCCESS (rc)
247 && !dbus_connection_add_filter (mContext->mConnection.get(),
248 dbusFilterFunction,
249 &mContext->mTriggered, NULL))
250 rc = VERR_NO_MEMORY;
251 if (RT_FAILURE (rc))
252 mContext->mConnection.reset();
253#endif /* defined RT_OS_LINUX && defined VBOX_WITH_DBUS */
254}
255
256/** Destructor */
257VBoxMainHotplugWaiter::~VBoxMainHotplugWaiter ()
258{
259#if defined RT_OS_LINUX && defined VBOX_WITH_DBUS
260 if (!!mContext->mConnection)
261 dbus_connection_remove_filter (mContext->mConnection.get(), dbusFilterFunction,
262 &mContext->mTriggered);
263 delete mContext;
264#endif /* defined RT_OS_LINUX && defined VBOX_WITH_DBUS */
265}
266
267/**
268 * Wait for a hotplug event.
269 *
270 * @returns VINF_SUCCESS if an event occurred or if Exit() was called.
271 * @returns VERR_TRY_AGAIN if something failed at the DBus level.
272 * @returns VERR_NOT_SUPPORTED if the service could not be contacted.
273 * @returns Possibly other iprt status codes otherwise.
274 */
275int VBoxMainHotplugWaiter::Wait()
276{
277 int rc = VINF_SUCCESS;
278#if defined RT_OS_LINUX && defined VBOX_WITH_DBUS
279 if (!mContext->mConnection)
280 rc = VERR_NOT_SUPPORTED;
281 bool connected = true;
282 mContext->mTriggered = false;
283 mContext->mInterrupt = false;
284 while ( RT_SUCCESS (rc) && connected && !mContext->mTriggered
285 && !mContext->mInterrupt)
286 connected = dbus_connection_read_write_dispatch (mContext->mConnection.get(),
287 DBUS_POLL_TIMEOUT);
288 if (!connected)
289 rc = VERR_TRY_AGAIN;
290#else /* !(defined RT_OS_LINUX && defined VBOX_WITH_DBUS) */
291 rc = VERR_NOT_IMPLEMENTED;
292#endif /* !(defined RT_OS_LINUX && defined VBOX_WITH_DBUS) */
293 return rc;
294}
295
296void VBoxMainHotplugWaiter::Interrupt()
297{
298#if defined RT_OS_LINUX && defined VBOX_WITH_DBUS
299 mContext->mInterrupt = true;
300#endif /* defined RT_OS_LINUX && defined VBOX_WITH_DBUS */
301}
302
303#ifdef RT_OS_LINUX
304/**
305 * Helper function to check whether the given device node is a valid drive
306 */
307/* static */
308bool validateDevice(const char *deviceNode, bool isDVD)
309{
310 AssertReturn(VALID_PTR (deviceNode), VERR_INVALID_POINTER);
311 LogFlowFunc (("deviceNode=%s, isDVD=%d\n", deviceNode, isDVD));
312 struct stat statInfo;
313 bool retValue = false;
314
315 // sanity check
316 if (!deviceNode)
317 {
318 return false;
319 }
320
321 // first a simple stat() call
322 if (stat(deviceNode, &statInfo) < 0)
323 {
324 return false;
325 } else
326 {
327 if (isDVD)
328 {
329 if (S_ISCHR(statInfo.st_mode) || S_ISBLK(statInfo.st_mode))
330 {
331 int fileHandle;
332 // now try to open the device
333 fileHandle = open(deviceNode, O_RDONLY | O_NONBLOCK, 0);
334 if (fileHandle >= 0)
335 {
336 cdrom_subchnl cdChannelInfo;
337 cdChannelInfo.cdsc_format = CDROM_MSF;
338 // this call will finally reveal the whole truth
339#ifdef RT_OS_LINUX
340 if ((ioctl(fileHandle, CDROMSUBCHNL, &cdChannelInfo) == 0) ||
341 (errno == EIO) || (errno == ENOENT) ||
342 (errno == EINVAL) || (errno == ENOMEDIUM))
343#endif
344 {
345 retValue = true;
346 }
347 close(fileHandle);
348 }
349 }
350 } else
351 {
352 // floppy case
353 if (S_ISCHR(statInfo.st_mode) || S_ISBLK(statInfo.st_mode))
354 {
355 /// @todo do some more testing, maybe a nice IOCTL!
356 retValue = true;
357 }
358 }
359 }
360 LogFlowFunc (("retValue=%d\n", retValue));
361 return retValue;
362}
363#else /* !RT_OS_LINUX */
364# error Port me! Copying code over from HostImpl.cpp should be most of the job though.
365#endif /* !RT_OS_LINUX */
366
367/**
368 * Extract the names of drives from an environment variable and add them to a
369 * list if they are valid.
370 * @returns iprt status code
371 * @param pszVar the name of the environment variable. The variable
372 * value should be a list of device node names, separated
373 * by ':' characters.
374 * @param pList the list to append the drives found to
375 * @param isDVD are we looking for DVD drives or for floppies?
376 * @param pfSuccess this will be set to true if we found at least one drive
377 * and to false otherwise. Optional.
378 */
379/* static */
380int getDriveInfoFromEnv(const char *pszVar, DriveInfoList *pList,
381 bool isDVD, bool *pfSuccess)
382{
383 AssertReturn( VALID_PTR (pszVar) && VALID_PTR (pList)
384 && (pfSuccess == NULL || VALID_PTR (pfSuccess)),
385 VERR_INVALID_POINTER);
386 LogFlowFunc (("pszVar=%s, pList=%p, isDVD=%d, pfSuccess=%p\n", pszVar,
387 pList, isDVD, pfSuccess));
388 int rc = VINF_SUCCESS;
389 bool success = false;
390 RTMemAutoPtr<char, RTStrFree> drive;
391 const char *pszValue = RTEnvGet (pszVar);
392 if (pszValue != NULL)
393 {
394 drive = RTStrDup (pszValue);
395 if (!drive)
396 rc = VERR_NO_MEMORY;
397 }
398 if (pszValue != NULL && RT_SUCCESS (rc))
399 {
400 char *pDrive = drive.get();
401 char *pDriveNext = strchr (pDrive, ':');
402 while (pDrive != NULL && *pDrive != '\0')
403 {
404 if (pDriveNext != NULL)
405 *pDriveNext = '\0';
406 if (validateDevice(pDrive, isDVD))
407 {
408 pList->push_back (DriveInfo (pDrive));
409 success = true;
410 }
411 if (pDriveNext != NULL)
412 {
413 pDrive = pDriveNext + 1;
414 pDriveNext = strchr (pDrive, ':');
415 }
416 else
417 pDrive = NULL;
418 }
419 }
420 if (pfSuccess != NULL)
421 *pfSuccess = success;
422 LogFlowFunc (("rc=%Rrc, success=%d\n", rc, success));
423 return rc;
424}
425
426#ifdef RT_OS_LINUX
427/**
428 * Helper function to parse the given mount file and add found entries
429 */
430/* static */
431int getDVDInfoFromMTab(char *mountTable, DriveInfoList *pList)
432{
433 AssertReturn(VALID_PTR (mountTable) && VALID_PTR (pList),
434 VERR_INVALID_POINTER);
435#ifdef RT_OS_LINUX
436 LogFlowFunc (("mountTable=%s, pList=%p\n", mountTable, pList));
437 int rc = VINF_SUCCESS;
438 FILE *mtab = setmntent(mountTable, "r");
439 if (mtab)
440 {
441 struct mntent *mntent;
442 RTMemAutoPtr <char, RTStrFree> mnt_type, mnt_dev;
443 char *tmp;
444 while (RT_SUCCESS (rc) && (mntent = getmntent(mtab)))
445 {
446 mnt_type = RTStrDup (mntent->mnt_type);
447 mnt_dev = RTStrDup (mntent->mnt_fsname);
448 if (!mnt_type || !mnt_dev)
449 rc = VERR_NO_MEMORY;
450 // supermount fs case
451 if (RT_SUCCESS (rc) && strcmp(mnt_type.get(), "supermount") == 0)
452 {
453 tmp = strstr(mntent->mnt_opts, "fs=");
454 if (tmp)
455 {
456 mnt_type = RTStrDup(tmp + strlen("fs="));
457 if (!mnt_type)
458 rc = VERR_NO_MEMORY;
459 else
460 {
461 tmp = strchr(mnt_type.get(), ',');
462 if (tmp)
463 *tmp = '\0';
464 }
465 }
466 tmp = strstr(mntent->mnt_opts, "dev=");
467 if (tmp)
468 {
469 mnt_dev = RTStrDup(tmp + strlen("dev="));
470 if (!mnt_dev)
471 rc = VERR_NO_MEMORY;
472 else
473 {
474 tmp = strchr(mnt_dev.get(), ',');
475 if (tmp)
476 *tmp = '\0';
477 }
478 }
479 }
480 // use strstr here to cover things fs types like "udf,iso9660"
481 if (RT_SUCCESS (rc) && strstr(mnt_type.get(), "iso9660") == 0)
482 {
483 if (validateDevice(mnt_dev.get(), true))
484 {
485 bool insert = true;
486 struct stat srcInfo;
487 if (stat (mnt_dev.get(), &srcInfo) < 0)
488 insert = false;
489 for (DriveInfoList::const_iterator it = pList->begin();
490 insert && it != pList->end(); ++it)
491 {
492 struct stat destInfo;
493 if ( (stat (it->mDevice.c_str(), &destInfo) == 0)
494 && (srcInfo.st_rdev == destInfo.st_rdev))
495 insert = false;
496 }
497 if (insert)
498 pList->push_back (DriveInfo (mnt_dev.get()));
499 }
500 }
501 }
502 endmntent(mtab);
503 }
504 return rc;
505#endif
506}
507
508#endif /* RT_OS_LINUX */
509
510#if defined(RT_OS_LINUX) && defined(VBOX_WITH_DBUS)
511/* Linux, load libdbus statically */
512
513/** Wrapper class around DBusError for automatic cleanup */
514class autoDBusError
515{
516 DBusError mError;
517public:
518 autoDBusError () { dbus_error_init (&mError); }
519 ~autoDBusError ()
520 {
521 if (IsSet())
522 dbus_error_free (&mError);
523 }
524 DBusError &get () { return mError; }
525 bool IsSet ()
526 {
527 Assert ((mError.name == NULL) == (mError.message == NULL));
528 return (mError.name != NULL);
529 }
530 bool HasName (const char *pszName)
531 {
532 Assert ((mError.name == NULL) == (mError.message == NULL));
533 return (RTStrCmp (mError.name, pszName) == 0);
534 }
535 void FlowLog ()
536 {
537 if (IsSet ())
538 LogFlow(("DBus error %s: %s\n", mError.name, mError.message));
539 }
540};
541
542/**
543 * Helper function for setting up a connection to hal
544 * @returns iprt status code
545 * @param ppConnection where to store the connection handle
546 */
547/* static */
548int halInit (RTMemAutoPtr <DBusConnection, halShutdown> *pConnection)
549{
550 AssertReturn(VALID_PTR (pConnection), VERR_INVALID_POINTER);
551 LogFlowFunc (("pConnection=%p\n", pConnection));
552 int rc = VINF_SUCCESS;
553 bool halSuccess = true;
554 autoDBusError dbusError;
555
556 RTMemAutoPtr <DBusConnection, VBoxDBusConnectionUnref> dbusConnection;
557 dbusConnection = dbus_bus_get (DBUS_BUS_SYSTEM, &dbusError.get());
558 if (!dbusConnection)
559 halSuccess = false;
560 if (halSuccess)
561 {
562 dbus_connection_set_exit_on_disconnect (dbusConnection.get(), false);
563 halSuccess = dbus_bus_name_has_owner (dbusConnection.get(),
564 "org.freedesktop.Hal", &dbusError.get());
565 }
566 if (halSuccess)
567 {
568 dbus_bus_add_match (dbusConnection.get(),
569 "type='signal',"
570 "interface='org.freedesktop.Hal.Manager',"
571 "sender='org.freedesktop.Hal',"
572 "path='/org/freedesktop/Hal/Manager'",
573 &dbusError.get());
574 halSuccess = !dbusError.IsSet();
575 }
576 if (dbusError.HasName (DBUS_ERROR_NO_MEMORY))
577 rc = VERR_NO_MEMORY;
578 if (halSuccess)
579 *pConnection = dbusConnection.release();
580 LogFlowFunc(("rc=%Rrc, (*pConnection).get()=%p\n", rc, (*pConnection).get()));
581 dbusError.FlowLog();
582 return rc;
583}
584
585/**
586 * Helper function for shutting down a connection to hal
587 * @param pConnection the connection handle
588 */
589/* static */
590void halShutdown (DBusConnection *pConnection)
591{
592 AssertReturnVoid(VALID_PTR (pConnection));
593 LogFlowFunc (("pConnection=%p\n", pConnection));
594 autoDBusError dbusError;
595
596 dbus_bus_remove_match (pConnection,
597 "type='signal',"
598 "interface='org.freedesktop.Hal.Manager',"
599 "sender='org.freedesktop.Hal',"
600 "path='/org/freedesktop/Hal/Manager'",
601 &dbusError.get());
602 dbus_connection_unref (pConnection);
603 LogFlowFunc(("returning\n"));
604 dbusError.FlowLog();
605}
606
607/**
608 * Find the UDIs of hal entries that contain Key=Value property.
609 * @returns iprt status code. If a non-fatal error occurs, we return success
610 * but reset pMessage to NULL.
611 * @param pConnection an initialised connection DBus
612 * @param pszKey the property key
613 * @param pszValue the property value
614 * @param pMessage where to store the return DBus message. This must be
615 * parsed to get at the UDIs. NOT optional.
616 */
617/* static */
618int halFindDeviceStringMatch (DBusConnection *pConnection, const char *pszKey,
619 const char *pszValue,
620 RTMemAutoPtr <DBusMessage, VBoxDBusMessageUnref> *pMessage)
621{
622 AssertReturn( VALID_PTR (pConnection) && VALID_PTR (pszKey)
623 && VALID_PTR (pszValue) && VALID_PTR (pMessage),
624 VERR_INVALID_POINTER);
625 LogFlowFunc (("pConnection=%p, pszKey=%s, pszValue=%s, pMessage=%p\n",
626 pConnection, pszKey, pszValue, pMessage));
627 int rc = VINF_SUCCESS; /* We set this to failure on fatal errors. */
628 bool halSuccess = true; /* We set this to false to abort the operation. */
629 autoDBusError dbusError;
630
631 RTMemAutoPtr <DBusMessage, VBoxDBusMessageUnref> message, reply;
632 if (halSuccess && RT_SUCCESS (rc))
633 {
634 message = dbus_message_new_method_call ("org.freedesktop.Hal",
635 "/org/freedesktop/Hal/Manager",
636 "org.freedesktop.Hal.Manager",
637 "FindDeviceStringMatch");
638 if (!message)
639 rc = VERR_NO_MEMORY;
640 }
641 if (halSuccess && RT_SUCCESS (rc))
642 {
643 DBusMessageIter iterAppend;
644 dbus_message_iter_init_append (message.get(), &iterAppend);
645 dbus_message_iter_append_basic (&iterAppend, DBUS_TYPE_STRING, &pszKey);
646 dbus_message_iter_append_basic (&iterAppend, DBUS_TYPE_STRING, &pszValue);
647 reply = dbus_connection_send_with_reply_and_block (pConnection,
648 message.get(), -1,
649 &dbusError.get());
650 if (!reply)
651 halSuccess = false;
652 }
653 *pMessage = reply.release ();
654 LogFlowFunc (("rc=%Rrc, *pMessage.value()=%p\n", rc, (*pMessage).get()));
655 dbusError.FlowLog();
656 return rc;
657}
658
659/**
660 * Read a set of string properties for a device. If some of the properties are
661 * not of type DBUS_TYPE_STRING then a NULL pointer will be returned for them.
662 * @returns iprt status code. If the operation failed for non-fatal reasons
663 * then we return success and leave pMessage untouched - reset it
664 * before the call to detect this.
665 * @param pConnection an initialised connection DBus
666 * @param pszUdi the Udi of the device
667 * @param cProps the number of property values to look up
668 * @param papszKeys the keys of the properties to be looked up
669 * @param papszValues where to store the values of the properties. The
670 * strings returned will be valid until the message
671 * returned in @a ppMessage is freed. Undefined if
672 * the message is NULL.
673 * @param pMessage where to store the return DBus message. The caller
674 * is responsible for freeing this once they have
675 * finished with the value strings. NOT optional.
676 */
677/* static */
678int halGetPropertyStrings (DBusConnection *pConnection, const char *pszUdi,
679 size_t cProps, const char **papszKeys,
680 char **papszValues,
681 RTMemAutoPtr <DBusMessage, VBoxDBusMessageUnref> *pMessage)
682{
683 AssertReturn( VALID_PTR (pConnection) && VALID_PTR (pszUdi)
684 && VALID_PTR (papszKeys) && VALID_PTR (papszValues)
685 && VALID_PTR (pMessage),
686 VERR_INVALID_POINTER);
687 LogFlowFunc (("pConnection=%p, pszUdi=%s, cProps=%llu, papszKeys=%p, papszValues=%p, pMessage=%p\n",
688 pConnection, pszUdi, cProps, papszKeys, papszValues, pMessage));
689 int rc = VINF_SUCCESS; /* We set this to failure on fatal errors. */
690 bool halSuccess = true; /* We set this to false to abort the operation. */
691 autoDBusError dbusError;
692
693 RTMemAutoPtr <DBusMessage, VBoxDBusMessageUnref> message, reply;
694 DBusMessageIter iterGet, iterProps, iterKey, iterValue;
695
696 /* Initialise the return array to NULLs */
697 for (size_t i = 0; i < cProps; ++i)
698 papszValues[i] = NULL;
699
700 /* Send a GetAllProperties message to hald */
701 message = dbus_message_new_method_call ("org.freedesktop.Hal", pszUdi,
702 "org.freedesktop.Hal.Device",
703 "GetAllProperties");
704 if (!message)
705 rc = VERR_NO_MEMORY;
706 if (halSuccess && RT_SUCCESS (rc))
707 {
708 reply = dbus_connection_send_with_reply_and_block (pConnection,
709 message.get(), -1,
710 &dbusError.get());
711 if (!reply)
712 halSuccess = false;
713 }
714
715 /* Parse the reply */
716 if (halSuccess && RT_SUCCESS (rc))
717 {
718 dbus_message_iter_init (reply.get(), &iterGet);
719 if ( dbus_message_iter_get_arg_type (&iterGet) != DBUS_TYPE_ARRAY
720 && dbus_message_iter_get_element_type (&iterGet) != DBUS_TYPE_DICT_ENTRY)
721 halSuccess = false;
722 }
723 if (halSuccess && RT_SUCCESS (rc))
724 dbus_message_iter_recurse (&iterGet, &iterProps);
725 /* Go through all entries in the reply and see if any match our keys. */
726 while ( halSuccess && RT_SUCCESS (rc)
727 && dbus_message_iter_get_arg_type (&iterProps)
728 == DBUS_TYPE_DICT_ENTRY)
729 {
730 const char *pszKey;
731 DBusMessageIter iterEntry, iterValue;
732 dbus_message_iter_recurse (&iterProps, &iterEntry);
733 dbus_message_iter_get_basic (&iterEntry, &pszKey);
734 dbus_message_iter_next (&iterEntry);
735 dbus_message_iter_recurse (&iterEntry, &iterValue);
736 /* Fill in any matches. */
737 for (size_t i = 0; i < cProps; ++i)
738 if (strcmp (pszKey, papszKeys[i]) == 0)
739 {
740 if (dbus_message_iter_get_arg_type (&iterValue) == DBUS_TYPE_STRING)
741 dbus_message_iter_get_basic (&iterValue, &papszValues[i]);
742 }
743 dbus_message_iter_next (&iterProps);
744 }
745 if (RT_SUCCESS (rc) && halSuccess)
746 *pMessage = reply.release();
747 if (dbusError.HasName (DBUS_ERROR_NO_MEMORY))
748 rc = VERR_NO_MEMORY;
749 LogFlowFunc (("rc=%Rrc, *pMessage.value()=%p\n", rc, (*pMessage).get()));
750 dbusError.FlowLog();
751 return rc;
752}
753
754/**
755 * Helper function to query the hal subsystem for information about drives
756 * attached to the system.
757 * @returns iprt status code
758 * @param pList where to add information about the drives detected
759 * @param isDVD are we looking for DVDs or floppies?
760 * @param pfSuccess will be set to true if all interactions with hal
761 * succeeded and to false otherwise. Optional.
762 *
763 * @returns IPRT status code
764 */
765/* static */
766int getDriveInfoFromHal(DriveInfoList *pList, bool isDVD, bool *pfSuccess)
767{
768 AssertReturn(VALID_PTR (pList) && (pfSuccess == NULL || VALID_PTR (pfSuccess)),
769 VERR_INVALID_POINTER);
770 LogFlowFunc (("pList=%p, isDVD=%d, pfSuccess=%p\n", pList, isDVD, pfSuccess));
771 int rc = VINF_SUCCESS; /* We set this to failure on fatal errors. */
772 bool halSuccess = true; /* We set this to false to abort the operation. */
773 autoDBusError dbusError;
774
775 RTMemAutoPtr <DBusMessage, VBoxDBusMessageUnref> message, replyFind, replyGet;
776 RTMemAutoPtr <DBusConnection, halShutdown> dbusConnection;
777 DBusMessageIter iterFind, iterUdis;
778
779 rc = halInit (&dbusConnection);
780 if (!dbusConnection)
781 halSuccess = false;
782 if (halSuccess && RT_SUCCESS (rc))
783 {
784 rc = halFindDeviceStringMatch (dbusConnection.get(), "storage.drive_type",
785 isDVD ? "cdrom" : "floppy", &replyFind);
786 if (!replyFind)
787 halSuccess = false;
788 }
789 if (halSuccess && RT_SUCCESS (rc))
790 {
791 dbus_message_iter_init (replyFind.get(), &iterFind);
792 if (dbus_message_iter_get_arg_type (&iterFind) != DBUS_TYPE_ARRAY)
793 halSuccess = false;
794 }
795 if (halSuccess && RT_SUCCESS (rc))
796 dbus_message_iter_recurse (&iterFind, &iterUdis);
797 for (; halSuccess && RT_SUCCESS (rc)
798 && dbus_message_iter_get_arg_type (&iterUdis) == DBUS_TYPE_STRING;
799 dbus_message_iter_next(&iterUdis))
800 {
801 /* Now get all properties from the iterator */
802 const char *pszUdi;
803 dbus_message_iter_get_basic (&iterUdis, &pszUdi);
804 static const char *papszKeys[] =
805 { "block.device", "info.product", "info.vendor" };
806 char *papszValues[RT_ELEMENTS (papszKeys)];
807 rc = halGetPropertyStrings (dbusConnection.get(), pszUdi, RT_ELEMENTS (papszKeys),
808 papszKeys, papszValues, &replyGet);
809 std::string description;
810 const char *pszDevice = papszValues[0], *pszProduct = papszValues[1],
811 *pszVendor = papszValues[2];
812 if (!!replyGet && pszDevice == NULL)
813 halSuccess = false;
814 if (!!replyGet && pszDevice != NULL)
815 {
816 if ((pszVendor != NULL) && (pszVendor[0] != '\0'))
817 (description += pszVendor) += " ";
818 if ((pszProduct != NULL && pszProduct[0] != '\0'))
819 description += pszProduct;
820 pList->push_back (DriveInfo (pszDevice, pszUdi, description));
821 }
822 }
823 if (dbusError.HasName (DBUS_ERROR_NO_MEMORY))
824 rc = VERR_NO_MEMORY;
825 if (pfSuccess != NULL)
826 *pfSuccess = halSuccess;
827 LogFlow (("rc=%Rrc, halSuccess=%d\n", rc, halSuccess));
828 dbusError.FlowLog();
829 return rc;
830}
831
832/**
833 * Helper function to query the hal subsystem for information about USB devices
834 * attached to the system.
835 * @returns iprt status code
836 * @param pList where to add information about the devices detected
837 * @param pfSuccess will be set to true if all interactions with hal
838 * succeeded and to false otherwise. Optional.
839 *
840 * @returns IPRT status code
841 */
842/* static */
843int getUSBDeviceInfoFromHal(USBDeviceInfoList *pList, bool *pfSuccess)
844{
845 AssertReturn(VALID_PTR (pList) && (pfSuccess == NULL || VALID_PTR (pfSuccess)),
846 VERR_INVALID_POINTER);
847 LogFlowFunc (("pList=%p, pfSuccess=%p\n", pList, pfSuccess));
848 int rc = VINF_SUCCESS; /* We set this to failure on fatal errors. */
849 bool halSuccess = true; /* We set this to false to abort the operation. */
850 autoDBusError dbusError;
851
852 RTMemAutoPtr <DBusMessage, VBoxDBusMessageUnref> message, replyFind, replyGet;
853 RTMemAutoPtr <DBusConnection, halShutdown> dbusConnection;
854 DBusMessageIter iterFind, iterUdis;
855
856 rc = halInit (&dbusConnection);
857 if (!dbusConnection)
858 halSuccess = false;
859 if (halSuccess && RT_SUCCESS (rc))
860 {
861 rc = halFindDeviceStringMatch (dbusConnection.get(), "info.subsystem",
862 "usb_device", &replyFind);
863 if (!replyFind)
864 halSuccess = false;
865 }
866 if (halSuccess && RT_SUCCESS (rc))
867 {
868 dbus_message_iter_init (replyFind.get(), &iterFind);
869 if (dbus_message_iter_get_arg_type (&iterFind) != DBUS_TYPE_ARRAY)
870 halSuccess = false;
871 }
872 if (halSuccess && RT_SUCCESS (rc))
873 dbus_message_iter_recurse (&iterFind, &iterUdis);
874 for (; halSuccess && RT_SUCCESS (rc)
875 && dbus_message_iter_get_arg_type (&iterUdis) == DBUS_TYPE_STRING;
876 dbus_message_iter_next(&iterUdis))
877 {
878 /* Now get the device node and the sysfs path from the iterator */
879 const char *pszUdi;
880 dbus_message_iter_get_basic (&iterUdis, &pszUdi);
881 static const char *papszKeys[] = { "linux.device_file", "linux.sysfs_path" };
882 char *papszValues[RT_ELEMENTS (papszKeys)];
883 rc = halGetPropertyStrings (dbusConnection.get(), pszUdi, RT_ELEMENTS (papszKeys),
884 papszKeys, papszValues, &replyGet);
885 std::string description;
886 const char *pszDevice = papszValues[0], *pszSysfsPath = papszValues[1];
887 /* Get the interfaces for this device. */
888 if (!!replyGet && pszDevice && pszSysfsPath)
889 {
890 USBDeviceInfo info (pszDevice, pszSysfsPath);
891 bool ifaceSuccess = true; /* If we can't get the interfaces, just
892 * skip this one device. */
893 rc = getUSBInterfacesFromHal (&info.mInterfaces, pszUdi, &ifaceSuccess);
894 if (RT_SUCCESS(rc) && halSuccess && ifaceSuccess)
895 pList->push_back (info);
896 }
897 }
898 if (dbusError.HasName (DBUS_ERROR_NO_MEMORY))
899 rc = VERR_NO_MEMORY;
900 if (pfSuccess != NULL)
901 *pfSuccess = halSuccess;
902 LogFlow (("rc=%Rrc, halSuccess=%d\n", rc, halSuccess));
903 dbusError.FlowLog();
904 return rc;
905}
906
907/**
908 * Helper function to query the hal subsystem for information about USB devices
909 * attached to the system.
910 * @returns iprt status code
911 * @param pList where to add information about the devices detected. If
912 * certain interfaces are not found (@a pfFound is false on
913 * return) this may contain invalid information.
914 * @param pcszUdi the hal UDI of the device
915 * @param pfSuccess will be set to true if the operation succeeds and to
916 * false if it fails for non-critical reasons. Optional.
917 *
918 * @returns IPRT status code
919 */
920/* static */
921int getUSBInterfacesFromHal(std::vector <std::string> *pList,
922 const char *pcszUdi, bool *pfSuccess)
923{
924 AssertReturn(VALID_PTR (pList) && VALID_PTR (pcszUdi) &&
925 (pfSuccess == NULL || VALID_PTR (pfSuccess)),
926 VERR_INVALID_POINTER);
927 LogFlowFunc (("pList=%p, pcszUdi=%s, pfSuccess=%p\n", pList, pcszUdi,
928 pfSuccess));
929 int rc = VINF_SUCCESS; /* We set this to failure on fatal errors. */
930 bool halSuccess = true; /* We set this to false to abort the operation. */
931 autoDBusError dbusError;
932
933 RTMemAutoPtr <DBusMessage, VBoxDBusMessageUnref> message, replyFind, replyGet;
934 RTMemAutoPtr <DBusConnection, halShutdown> dbusConnection;
935 DBusMessageIter iterFind, iterUdis;
936
937 rc = halInit (&dbusConnection);
938 if (!dbusConnection)
939 halSuccess = false;
940 if (halSuccess && RT_SUCCESS (rc))
941 {
942 /* Look for children of the current UDI. */
943 rc = halFindDeviceStringMatch (dbusConnection.get(), "info.parent",
944 pcszUdi, &replyFind);
945 if (!replyFind)
946 halSuccess = false;
947 }
948 if (halSuccess && RT_SUCCESS (rc))
949 {
950 dbus_message_iter_init (replyFind.get(), &iterFind);
951 if (dbus_message_iter_get_arg_type (&iterFind) != DBUS_TYPE_ARRAY)
952 halSuccess = false;
953 }
954 if (halSuccess && RT_SUCCESS (rc))
955 dbus_message_iter_recurse (&iterFind, &iterUdis);
956 for (; halSuccess && RT_SUCCESS (rc)
957 && dbus_message_iter_get_arg_type (&iterUdis) == DBUS_TYPE_STRING;
958 dbus_message_iter_next(&iterUdis))
959 {
960 /* Now get the sysfs path and the subsystem from the iterator */
961 const char *pszUdi;
962 dbus_message_iter_get_basic (&iterUdis, &pszUdi);
963 static const char *papszKeys[] = { "linux.sysfs_path", "info.subsystem" };
964 char *papszValues[RT_ELEMENTS (papszKeys)];
965 rc = halGetPropertyStrings (dbusConnection.get(), pszUdi, RT_ELEMENTS (papszKeys),
966 papszKeys, papszValues, &replyGet);
967 std::string description;
968 const char *pszSysfsPath = papszValues[0], *pszSubsystem = papszValues[1];
969 if (!replyGet)
970 halSuccess = false;
971 if (!!replyGet && pszSysfsPath == NULL)
972 halSuccess = false;
973 if ( halSuccess && RT_SUCCESS (rc)
974 && RTStrCmp (pszSubsystem, "usb_device") != 0) /* Children of buses can also be devices. */
975 pList->push_back (pszSysfsPath);
976 }
977 if (dbusError.HasName (DBUS_ERROR_NO_MEMORY))
978 rc = VERR_NO_MEMORY;
979 if (pfSuccess != NULL)
980 *pfSuccess = halSuccess;
981 LogFlow (("rc=%Rrc, halSuccess=%d\n", rc, halSuccess));
982 dbusError.FlowLog();
983 return rc;
984}
985
986/**
987 * When it is registered with DBus, this function will be called by
988 * dbus_connection_read_write_dispatch each time a message is received over the
989 * DBus connection. We check whether that message was caused by a hal device
990 * hotplug event, and if so we set a flag. dbus_connection_read_write_dispatch
991 * will return after calling its filter functions, and its caller should then
992 * check the status of the flag passed to the filter function.
993 *
994 * @param pConnection The DBus connection we are using.
995 * @param pMessage The DBus message which just arrived.
996 * @param pvUser A pointer to the flag variable we are to set.
997 */
998/* static */
999DBusHandlerResult dbusFilterFunction (DBusConnection *pConnection,
1000 DBusMessage *pMessage, void *pvUser)
1001{
1002 bool *pTriggered = reinterpret_cast<bool *> (pvUser);
1003 if ( dbus_message_is_signal (pMessage, "org.freedesktop.Hal.Manager",
1004 "DeviceAdded")
1005 || dbus_message_is_signal (pMessage, "org.freedesktop.Hal.Manager",
1006 "DeviceRemoved"))
1007 {
1008 *pTriggered = true;
1009 }
1010 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1011}
1012#endif /* RT_OS_LINUX && VBOX_WITH_DBUS */
1013
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