VirtualBox

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

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

Main/HostHardwareLinux: function descriptions and removed a header file

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