VirtualBox

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

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

Main: add support for polling for USB devices on Linux hosts without usbfs or hal

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 89.4 KB
Line 
1/* $Id: HostHardwareLinux.cpp 28553 2010-04-21 10:16:57Z vboxsync $ */
2/** @file
3 * Classes for handling hardware detection under Linux. Please feel free to
4 * expand these to work for other systems (Solaris!) or to add new ones for
5 * other systems.
6 */
7
8/*
9 * Copyright (C) 2008 Sun Microsystems, Inc.
10 *
11 * This file is part of VirtualBox Open Source Edition (OSE), as
12 * available from http://www.215389.xyz. This file is free software;
13 * you can redistribute it and/or modify it under the terms of the GNU
14 * General Public License (GPL) as published by the Free Software
15 * Foundation, in version 2 as it comes in the "COPYING" file of the
16 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
17 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
18 *
19 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
20 * Clara, CA 95054 USA or visit http://www.sun.com if you need
21 * additional information or have any questions.
22 */
23
24#define LOG_GROUP LOG_GROUP_MAIN
25
26/*******************************************************************************
27* Header Files *
28*******************************************************************************/
29
30#include <HostHardwareLinux.h>
31
32#include <VBox/log.h>
33# ifdef VBOX_WITH_DBUS
34# include <VBox/dbus.h>
35# endif
36
37#include <iprt/dir.h>
38#include <iprt/env.h>
39#include <iprt/file.h>
40#include <iprt/mem.h>
41#include <iprt/param.h>
42#include <iprt/path.h>
43#include <iprt/pipe.h>
44#include <iprt/poll.h>
45#include <iprt/socket.h>
46#include <iprt/string.h>
47#include <iprt/thread.h> /* for RTThreadSleep() */
48
49#ifdef RT_OS_LINUX
50# include <sys/types.h>
51# include <sys/stat.h>
52# include <unistd.h>
53# include <fcntl.h>
54/* bird: This is a hack to work around conflicts between these linux kernel headers
55 * and the GLIBC tcpip headers. They have different declarations of the 4
56 * standard byte order functions. */
57// # define _LINUX_BYTEORDER_GENERIC_H
58# define _LINUX_BYTEORDER_SWABB_H
59# include <linux/cdrom.h>
60# include <linux/fd.h>
61# include <linux/major.h>
62# include <errno.h>
63# include <scsi/scsi.h>
64
65# include <iprt/linux/sysfs.h>
66#endif /* RT_OS_LINUX */
67
68#include <fam.h>
69
70#include <vector>
71
72/******************************************************************************
73* Global Variables *
74******************************************************************************/
75
76#ifdef TESTCASE
77static bool testing() { return true; }
78static bool fNoProbe = false;
79static bool noProbe() { return fNoProbe; }
80static void setNoProbe(bool val) { fNoProbe = val; }
81#else
82static bool testing() { return false; }
83static bool noProbe() { return false; }
84static void setNoProbe(bool val) { (void)val; }
85#endif
86
87/******************************************************************************
88* Typedefs and Defines *
89******************************************************************************/
90
91/** When waiting for hotplug events, we currently restart the wait after at
92 * most this many milliseconds. */
93enum { DBUS_POLL_TIMEOUT = 2000 /* ms */ };
94
95static int getDriveInfoFromEnv(const char *pcszVar, DriveInfoList *pList,
96 bool isDVD, bool *pfSuccess);
97static int getDriveInfoFromDev(DriveInfoList *pList, bool isDVD,
98 bool *pfSuccess);
99static int getDriveInfoFromSysfs(DriveInfoList *pList, bool isDVD,
100 bool *pfSuccess);
101static int getUSBDeviceInfoFromSysfs(USBDeviceInfoList *pList, bool *pfSuccess);
102#ifdef VBOX_WITH_DBUS
103/* These must be extern to be usable in the RTMemAutoPtr template */
104extern void VBoxHalShutdown (DBusConnection *pConnection);
105extern void VBoxHalShutdownPrivate (DBusConnection *pConnection);
106extern void VBoxDBusConnectionUnref(DBusConnection *pConnection);
107extern void VBoxDBusConnectionCloseAndUnref(DBusConnection *pConnection);
108extern void VBoxDBusMessageUnref(DBusMessage *pMessage);
109
110static int halInit(RTMemAutoPtr <DBusConnection, VBoxHalShutdown> *pConnection);
111static int halInitPrivate(RTMemAutoPtr <DBusConnection, VBoxHalShutdownPrivate> *pConnection);
112static int halFindDeviceStringMatch (DBusConnection *pConnection,
113 const char *pszKey, const char *pszValue,
114 RTMemAutoPtr <DBusMessage, VBoxDBusMessageUnref> *pMessage);
115/*
116static int halFindDeviceStringMatchVector (DBusConnection *pConnection,
117 const char *pszKey,
118 const char *pszValue,
119 std::vector<iprt::MiniString> *pMatches);
120*/
121static int halGetPropertyStrings (DBusConnection *pConnection,
122 const char *pszUdi, size_t cKeys,
123 const char **papszKeys, char **papszValues,
124 RTMemAutoPtr <DBusMessage, VBoxDBusMessageUnref> *pMessage);
125/*
126static int halGetPropertyStringsVector (DBusConnection *pConnection,
127 const char *pszUdi, size_t cProps,
128 const char **papszKeys,
129 std::vector<iprt::MiniString> *pMatches,
130 bool *pfMatches, bool *pfSuccess);
131*/
132static int getUSBDeviceInfoFromHal(USBDeviceInfoList *pList, bool *pfSuccess);
133static int getOldUSBDeviceInfoFromHal(USBDeviceInfoList *pList, bool *pfSuccess);
134static int getUSBInterfacesFromHal(std::vector <iprt::MiniString> *pList,
135 const char *pcszUdi, bool *pfSuccess);
136static DBusHandlerResult dbusFilterFunction (DBusConnection *pConnection,
137 DBusMessage *pMessage, void *pvUser);
138#endif /* VBOX_WITH_DBUS */
139
140
141/** Find the length of a string, ignoring trailing non-ascii or control
142 * characters */
143static size_t strLenStripped(const char *pcsz)
144{
145 size_t cch = 0;
146 for (size_t i = 0; pcsz[i] != '\0'; ++i)
147 if (pcsz[i] > 32 && pcsz[i] < 127)
148 cch = i;
149 return cch + 1;
150}
151
152
153/**
154 * Get the name of a floppy drive according to the Linux floppy driver.
155 * @returns true on success, false if the name was not available (i.e. the
156 * device was not readible, or the file name wasn't a PC floppy
157 * device)
158 * @param pcszNode the path to the device node for the device
159 * @param Number the Linux floppy driver number for the drive. Required.
160 * @param pszName where to store the name retreived
161 */
162static bool floppyGetName(const char *pcszNode, unsigned Number,
163 floppy_drive_name pszName)
164{
165 AssertPtrReturn(pcszNode, false);
166 AssertPtrReturn(pszName, false);
167 AssertReturn(Number <= 7, false);
168 RTFILE File;
169 int rc = RTFileOpen(&File, pcszNode, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE | RTFILE_O_NON_BLOCK);
170 if (RT_SUCCESS(rc))
171 {
172 int rcIoCtl;
173 /** @todo The next line can produce a warning, as the ioctl request
174 * field is defined as signed, but the Linux ioctl definition macros
175 * produce unsigned constants. */
176 rc = RTFileIoCtl(File, FDGETDRVTYP, pszName, 0, &rcIoCtl);
177 RTFileClose(File);
178 if (RT_SUCCESS(rc) && rcIoCtl >= 0)
179 return true;
180 }
181 return false;
182}
183
184
185/**
186 * Create a UDI and a description for a floppy drive based on a number and the
187 * driver's name for it. We deliberately return an ugly sequence of
188 * characters as the description rather than an English language string to
189 * avoid translation issues.
190 *
191 * @returns true if we know the device to be valid, false otherwise
192 * @param pcszName the floppy driver name for the device (optional)
193 * @param Number the number of the floppy (0 to 3 on FDC 0, 4 to 7 on
194 * FDC 1)
195 * @param pszDesc where to store the device description (optional)
196 * @param cchDesc the size of the buffer in @a pszDesc
197 * @param pszUdi where to store the device UDI (optional)
198 * @param cchUdi the size of the buffer in @a pszUdi
199 */
200static void floppyCreateDeviceStrings(const floppy_drive_name pcszName,
201 unsigned Number, char *pszDesc,
202 size_t cchDesc, char *pszUdi,
203 size_t cchUdi)
204{
205 AssertPtrNullReturnVoid(pcszName);
206 AssertPtrNullReturnVoid(pszDesc);
207 AssertReturnVoid(!pszDesc || cchDesc > 0);
208 AssertPtrNullReturnVoid(pszUdi);
209 AssertReturnVoid(!pszUdi || cchUdi > 0);
210 AssertReturnVoid(Number <= 7);
211 if (pcszName)
212 {
213 const char *pcszSize;
214 switch(pcszName[0])
215 {
216 case 'd': case 'q': case 'h':
217 pcszSize = "5.25\"";
218 break;
219 case 'D': case 'H': case 'E': case 'u':
220 pcszSize = "3.5\"";
221 break;
222 default:
223 pcszSize = "(unknown)";
224 }
225 if (pszDesc)
226 RTStrPrintf(pszDesc, cchDesc, "%s %s K%s", pcszSize, &pcszName[1],
227 Number > 3 ? ", FDC 2" : "");
228 }
229 else
230 {
231 if (pszDesc)
232 RTStrPrintf(pszDesc, cchDesc, "FDD %d%s", (Number & 4) + 1,
233 Number > 3 ? ", FDC 2" : "");
234 }
235 if (pszUdi)
236 RTStrPrintf(pszUdi, cchUdi,
237 "/org/freedesktop/Hal/devices/platform_floppy_%u_storage",
238 Number);
239}
240
241
242/**
243 * Check whether a device number might correspond to a CD-ROM device according
244 * to Documentation/devices.txt in the Linux kernel source.
245 * @returns true if it might, false otherwise
246 * @param Number the device number (major and minor combination)
247 */
248static bool isCdromDevNum(dev_t Number)
249{
250 int major = major(Number);
251 int minor = minor(Number);
252 if ((major == IDE0_MAJOR) && !(minor & 0x3f))
253 return true;
254 if (major == SCSI_CDROM_MAJOR)
255 return true;
256 if (major == CDU31A_CDROM_MAJOR)
257 return true;
258 if (major == GOLDSTAR_CDROM_MAJOR)
259 return true;
260 if (major == OPTICS_CDROM_MAJOR)
261 return true;
262 if (major == SANYO_CDROM_MAJOR)
263 return true;
264 if (major == MITSUMI_X_CDROM_MAJOR)
265 return true;
266 if ((major == IDE1_MAJOR) && !(minor & 0x3f))
267 return true;
268 if (major == MITSUMI_CDROM_MAJOR)
269 return true;
270 if (major == CDU535_CDROM_MAJOR)
271 return true;
272 if (major == MATSUSHITA_CDROM_MAJOR)
273 return true;
274 if (major == MATSUSHITA_CDROM2_MAJOR)
275 return true;
276 if (major == MATSUSHITA_CDROM3_MAJOR)
277 return true;
278 if (major == MATSUSHITA_CDROM4_MAJOR)
279 return true;
280 if (major == AZTECH_CDROM_MAJOR)
281 return true;
282 if (major == 30 /* CM205_CDROM_MAJOR */) /* no #define for some reason */
283 return true;
284 if (major == CM206_CDROM_MAJOR)
285 return true;
286 if ((major == IDE3_MAJOR) && !(minor & 0x3f))
287 return true;
288 if (major == 46 /* Parallel port ATAPI CD-ROM */) /* no #define */
289 return true;
290 if ((major == IDE4_MAJOR) && !(minor & 0x3f))
291 return true;
292 if ((major == IDE5_MAJOR) && !(minor & 0x3f))
293 return true;
294 if ((major == IDE6_MAJOR) && !(minor & 0x3f))
295 return true;
296 if ((major == IDE7_MAJOR) && !(minor & 0x3f))
297 return true;
298 if ((major == IDE8_MAJOR) && !(minor & 0x3f))
299 return true;
300 if ((major == IDE9_MAJOR) && !(minor & 0x3f))
301 return true;
302 if (major == 113 /* VIOCD_MAJOR */)
303 return true;
304 return false;
305}
306
307
308/**
309 * Send an SCSI INQUIRY command to a device and return selected information.
310 * @returns iprt status code
311 * @returns VERR_TRY_AGAIN if the query failed but might succeed next time
312 * @param pcszNode the full path to the device node
313 * @param pu8Type where to store the SCSI device type on success (optional)
314 * @param pchVendor where to store the vendor id string on success (optional)
315 * @param cchVendor the size of the @a pchVendor buffer
316 * @param pchModel where to store the product id string on success (optional)
317 * @param cchModel the size of the @a pchModel buffer
318 * @note check documentation on the SCSI INQUIRY command and the Linux kernel
319 * SCSI headers included above if you want to understand what is going
320 * on in this method.
321 */
322static int cdromDoInquiry(const char *pcszNode, uint8_t *pu8Type,
323 char *pchVendor, size_t cchVendor, char *pchModel,
324 size_t cchModel)
325{
326 LogRelFlowFunc(("pcszNode=%s, pu8Type=%p, pchVendor=%p, cchVendor=%llu, pchModel=%p, cchModel=%llu\n",
327 pcszNode, pu8Type, pchVendor, cchVendor, pchModel,
328 cchModel));
329 AssertPtrReturn(pcszNode, VERR_INVALID_POINTER);
330 AssertPtrNullReturn(pu8Type, VERR_INVALID_POINTER);
331 AssertPtrNullReturn(pchVendor, VERR_INVALID_POINTER);
332 AssertPtrNullReturn(pchModel, VERR_INVALID_POINTER);
333
334 RTFILE hFile;
335 int rc = RTFileOpen(&hFile, pcszNode, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE | RTFILE_O_NON_BLOCK);
336 if (RT_SUCCESS(rc))
337 {
338 int rcIoCtl = 0;
339 unsigned char u8Response[96] = { 0 };
340 struct cdrom_generic_command CdromCommandReq;
341 RT_ZERO(CdromCommandReq);
342 CdromCommandReq.cmd[0] = INQUIRY;
343 CdromCommandReq.cmd[4] = sizeof(u8Response);
344 CdromCommandReq.buffer = u8Response;
345 CdromCommandReq.buflen = sizeof(u8Response);
346 CdromCommandReq.data_direction = CGC_DATA_READ;
347 CdromCommandReq.timeout = 5000; /* ms */
348 rc = RTFileIoCtl(hFile, CDROM_SEND_PACKET, &CdromCommandReq, 0, &rcIoCtl);
349 if (RT_SUCCESS(rc) && rcIoCtl < 0)
350 rc = RTErrConvertFromErrno(-CdromCommandReq.stat);
351 RTFileClose(hFile);
352
353 if (RT_SUCCESS(rc))
354 {
355 if (pu8Type)
356 *pu8Type = u8Response[0] & 0x1f;
357 if (pchVendor)
358 RTStrPrintf(pchVendor, cchVendor, "%.8s",
359 &u8Response[8] /* vendor id string */);
360 if (pchModel)
361 RTStrPrintf(pchModel, cchModel, "%.16s",
362 &u8Response[16] /* product id string */);
363 LogRelFlowFunc(("returning success: type=%u, vendor=%.8s, product=%.16s\n",
364 u8Response[0] & 0x1f, &u8Response[8], &u8Response[16]));
365 return VINF_SUCCESS;
366 }
367 }
368 LogRelFlowFunc(("returning %Rrc\n", rc));
369 return rc;
370}
371
372
373/**
374 * Initialise the device strings (description and UDI) for a DVD drive based on
375 * vendor and model name strings.
376 * @param pcszVendor the vendor ID string
377 * @param pcszModel the product ID string
378 * @param pszDesc where to store the description string (optional)
379 * @param cchDesc the size of the buffer in @pszDesc
380 * @param pszUdi where to store the UDI string (optional)
381 * @param cchUdi the size of the buffer in @pszUdi
382 */
383/* static */
384void dvdCreateDeviceStrings(const char *pcszVendor, const char *pcszModel,
385 char *pszDesc, size_t cchDesc, char *pszUdi,
386 size_t cchUdi)
387{
388 AssertPtrReturnVoid(pcszVendor);
389 AssertPtrReturnVoid(pcszModel);
390 AssertPtrNullReturnVoid(pszDesc);
391 AssertReturnVoid(!pszDesc || cchDesc > 0);
392 AssertPtrNullReturnVoid(pszUdi);
393 AssertReturnVoid(!pszUdi || cchUdi > 0);
394 char szCleaned[128];
395 size_t cchVendor = strLenStripped(pcszVendor);
396 size_t cchModel = strLenStripped(pcszModel);
397
398 /* Create a cleaned version of the model string for the UDI string. */
399 for (unsigned i = 0; pcszModel[i] != '\0' && i < sizeof(szCleaned); ++i)
400 if ( (pcszModel[i] >= '0' && pcszModel[i] <= '9')
401 || (pcszModel[i] >= 'A' && pcszModel[i] <= 'z'))
402 szCleaned[i] = pcszModel[i];
403 else
404 szCleaned[i] = '_';
405 szCleaned[RT_MIN(cchModel, sizeof(szCleaned) - 1)] = '\0';
406
407 /* Construct the description string as "Vendor Product" */
408 if (pszDesc)
409 {
410 if (cchVendor > 0)
411 RTStrPrintf(pszDesc, cchDesc, "%.*s %s", cchVendor, pcszVendor,
412 cchModel > 0 ? pcszModel : "(unknown drive model)");
413 else
414 RTStrPrintf(pszDesc, cchDesc, "%s", pcszModel);
415 }
416 /* Construct the UDI string */
417 if (pszUdi)
418 {
419 if (cchModel > 0)
420 RTStrPrintf(pszUdi, cchUdi,
421 "/org/freedesktop/Hal/devices/storage_model_%s",
422 szCleaned);
423 else
424 pszUdi[0] = '\0';
425 }
426}
427
428
429/**
430 * Check whether a device node points to a valid device and create a UDI and
431 * a description for it, and store the device number, if it does.
432 * @returns true if the device is valid, false otherwise
433 * @param pcszNode the path to the device node
434 * @param isDVD are we looking for a DVD device (or a floppy device)?
435 * @param pDevice where to store the device node (optional)
436 * @param pszDesc where to store the device description (optional)
437 * @param cchDesc the size of the buffer in @a pszDesc
438 * @param pszUdi where to store the device UDI (optional)
439 * @param cchUdi the size of the buffer in @a pszUdi
440 */
441static bool devValidateDevice(const char *pcszNode, bool isDVD, dev_t *pDevice,
442 char *pszDesc, size_t cchDesc, char *pszUdi,
443 size_t cchUdi)
444{
445 AssertPtrReturn(pcszNode, false);
446 AssertPtrNullReturn(pDevice, false);
447 AssertPtrNullReturn(pszDesc, false);
448 AssertReturn(!pszDesc || cchDesc > 0, false);
449 AssertPtrNullReturn(pszUdi, false);
450 AssertReturn(!pszUdi || cchUdi > 0, false);
451 RTFSOBJINFO ObjInfo;
452 if (RT_FAILURE(RTPathQueryInfo(pcszNode, &ObjInfo, RTFSOBJATTRADD_UNIX)))
453 return false;
454 if (!RTFS_IS_DEV_BLOCK(ObjInfo.Attr.fMode))
455 return false;
456 if (pDevice)
457 *pDevice = ObjInfo.Attr.u.Unix.Device;
458 if (isDVD)
459 {
460 char szVendor[128], szModel[128];
461 uint8_t u8Type;
462 if (!isCdromDevNum(ObjInfo.Attr.u.Unix.Device))
463 return false;
464 if (RT_FAILURE(cdromDoInquiry(pcszNode, &u8Type,
465 szVendor, sizeof(szVendor),
466 szModel, sizeof(szModel))))
467 return false;
468 if (u8Type != TYPE_ROM)
469 return false;
470 dvdCreateDeviceStrings(szVendor, szModel, pszDesc, cchDesc,
471 pszUdi, cchUdi);
472 }
473 else
474 {
475 /* Floppies on Linux are legacy devices with hardcoded majors and
476 * minors */
477 unsigned Number;
478 floppy_drive_name szName;
479 if (major(ObjInfo.Attr.u.Unix.Device) != FLOPPY_MAJOR)
480 return false;
481 switch (minor(ObjInfo.Attr.u.Unix.Device))
482 {
483 case 0: case 1: case 2: case 3:
484 Number = minor(ObjInfo.Attr.u.Unix.Device);
485 break;
486 case 128: case 129: case 130: case 131:
487 Number = minor(ObjInfo.Attr.u.Unix.Device) - 128 + 4;
488 break;
489 default:
490 return false;
491 }
492 if (!floppyGetName(pcszNode, Number, szName))
493 return false;
494 floppyCreateDeviceStrings(szName, Number, pszDesc, cchDesc, pszUdi,
495 cchUdi);
496 }
497 return true;
498}
499
500
501int VBoxMainDriveInfo::updateDVDs ()
502{
503 LogFlowThisFunc(("entered\n"));
504 int rc = VINF_SUCCESS;
505 bool success = false; /* Have we succeeded in finding anything yet? */
506 try
507 {
508 mDVDList.clear ();
509 /* Always allow the user to override our auto-detection using an
510 * environment variable. */
511 if (RT_SUCCESS(rc) && (!success || testing()))
512 rc = getDriveInfoFromEnv ("VBOX_CDROM", &mDVDList, true /* isDVD */,
513 &success);
514 setNoProbe(false);
515 if (RT_SUCCESS(rc) && (!success | testing()))
516 rc = getDriveInfoFromSysfs(&mDVDList, true /* isDVD */, &success);
517 if (RT_SUCCESS(rc) && testing())
518 {
519 setNoProbe(true);
520 rc = getDriveInfoFromSysfs(&mDVDList, true /* isDVD */, &success);
521 }
522 /* Walk through the /dev subtree if nothing else has helped. */
523 if (RT_SUCCESS(rc) && (!success | testing()))
524 rc = getDriveInfoFromDev(&mDVDList, true /* isDVD */, &success);
525 }
526 catch(std::bad_alloc &e)
527 {
528 rc = VERR_NO_MEMORY;
529 }
530 LogFlowThisFunc(("rc=%Rrc\n", rc));
531 return rc;
532}
533
534int VBoxMainDriveInfo::updateFloppies ()
535{
536 LogFlowThisFunc(("entered\n"));
537 int rc = VINF_SUCCESS;
538 bool success = false; /* Have we succeeded in finding anything yet? */
539 try
540 {
541 mFloppyList.clear ();
542 if (RT_SUCCESS(rc) && (!success || testing()))
543 rc = getDriveInfoFromEnv("VBOX_FLOPPY", &mFloppyList,
544 false /* isDVD */, &success);
545 setNoProbe(false);
546 if ( RT_SUCCESS(rc) && (!success || testing()))
547 rc = getDriveInfoFromSysfs(&mFloppyList, false /* isDVD */,
548 &success);
549 if (RT_SUCCESS(rc) && testing())
550 {
551 setNoProbe(true);
552 rc = getDriveInfoFromSysfs(&mFloppyList, false /* isDVD */, &success);
553 }
554 /* Walk through the /dev subtree if nothing else has helped. */
555 if ( RT_SUCCESS(rc) && (!success || testing()))
556 rc = getDriveInfoFromDev(&mFloppyList, false /* isDVD */,
557 &success);
558 }
559 catch(std::bad_alloc &e)
560 {
561 rc = VERR_NO_MEMORY;
562 }
563 LogFlowThisFunc(("rc=%Rrc\n", rc));
564 return rc;
565}
566
567
568/**
569 * Extract the names of drives from an environment variable and add them to a
570 * list if they are valid.
571 * @returns iprt status code
572 * @param pcszVar the name of the environment variable. The variable
573 * value should be a list of device node names, separated
574 * by ':' characters.
575 * @param pList the list to append the drives found to
576 * @param isDVD are we looking for DVD drives or for floppies?
577 * @param pfSuccess this will be set to true if we found at least one drive
578 * and to false otherwise. Optional.
579 */
580/* static */
581int getDriveInfoFromEnv(const char *pcszVar, DriveInfoList *pList,
582 bool isDVD, bool *pfSuccess)
583{
584 AssertPtrReturn(pcszVar, VERR_INVALID_POINTER);
585 AssertPtrReturn(pList, VERR_INVALID_POINTER);
586 AssertPtrNullReturn(pfSuccess, VERR_INVALID_POINTER);
587 LogFlowFunc(("pcszVar=%s, pList=%p, isDVD=%d, pfSuccess=%p\n", pcszVar,
588 pList, isDVD, pfSuccess));
589 int rc = VINF_SUCCESS;
590 bool success = false;
591 char *pszFreeMe = RTEnvDupEx(RTENV_DEFAULT, pcszVar);
592
593 try
594 {
595 const char *pcszCurrent = pszFreeMe;
596 while (pcszCurrent && *pcszCurrent != '\0')
597 {
598 const char *pcszNext = strchr(pcszCurrent, ':');
599 char szPath[RTPATH_MAX], szReal[RTPATH_MAX];
600 char szDesc[256], szUdi[256];
601 if (pcszNext)
602 RTStrPrintf(szPath, sizeof(szPath), "%.*s",
603 pcszNext - pcszCurrent - 1, pcszCurrent);
604 else
605 RTStrPrintf(szPath, sizeof(szPath), "%s", pcszCurrent);
606 if ( RT_SUCCESS(RTPathReal(szPath, szReal, sizeof(szReal)))
607 && devValidateDevice(szReal, isDVD, NULL, szDesc,
608 sizeof(szDesc), szUdi, sizeof(szUdi)))
609 {
610 pList->push_back(DriveInfo(szReal, szUdi, szDesc));
611 success = true;
612 }
613 pcszCurrent = pcszNext ? pcszNext + 1 : NULL;
614 }
615 if (pfSuccess != NULL)
616 *pfSuccess = success;
617 }
618 catch(std::bad_alloc &e)
619 {
620 rc = VERR_NO_MEMORY;
621 }
622 RTStrFree(pszFreeMe);
623 LogFlowFunc(("rc=%Rrc, success=%d\n", rc, success));
624 return rc;
625}
626
627
628class sysfsBlockDev
629{
630public:
631 sysfsBlockDev(const char *pcszName, bool wantDVD)
632 : mpcszName(pcszName), mwantDVD(wantDVD), misConsistent(true),
633 misValid(false)
634 {
635 if (findDeviceNode())
636 {
637 if (mwantDVD)
638 validateAndInitForDVD();
639 else
640 validateAndInitForFloppy();
641 }
642 }
643private:
644 /** The name of the subdirectory of /sys/block for this device */
645 const char *mpcszName;
646 /** Are we looking for a floppy or a DVD device? */
647 bool mwantDVD;
648 /** The device node for the device */
649 char mszNode[RTPATH_MAX];
650 /** Does the sysfs entry look like we expect it too? This is a canary
651 * for future sysfs ABI changes. */
652 bool misConsistent;
653 /** Is this entry a valid specimen of what we are looking for? */
654 bool misValid;
655 /** Human readible drive description string */
656 char mszDesc[256];
657 /** Unique identifier for the drive. Should be identical to hal's UDI for
658 * the device. May not be unique for two identical drives. */
659 char mszUdi[256];
660private:
661 /* Private methods */
662
663 /**
664 * Fill in the device node member based on the /sys/block subdirectory.
665 * @returns boolean success value
666 */
667 bool findDeviceNode()
668 {
669 dev_t dev = RTLinuxSysFsReadDevNumFile("block/%s/dev", mpcszName);
670 if (dev == 0)
671 {
672 misConsistent = false;
673 return false;
674 }
675 if (RTLinuxFindDevicePath(dev, RTFS_TYPE_DEV_BLOCK, mszNode,
676 sizeof(mszNode), "%s", mpcszName) < 0)
677 return false;
678 return true;
679 }
680
681 /** Check whether the sysfs block entry is valid for a DVD device and
682 * initialise the string data members for the object. We try to get all
683 * the information we need from sysfs if possible, to avoid unnecessarily
684 * poking the device, and if that fails we fall back to an SCSI INQUIRY
685 * command. */
686 void validateAndInitForDVD()
687 {
688 char szVendor[128], szModel[128];
689 ssize_t cchVendor, cchModel;
690 int64_t type = RTLinuxSysFsReadIntFile(10, "block/%s/device/type",
691 mpcszName);
692 if (type >= 0 && type != TYPE_ROM)
693 return;
694 if (type == TYPE_ROM)
695 {
696 cchVendor = RTLinuxSysFsReadStrFile(szVendor, sizeof(szVendor),
697 "block/%s/device/vendor",
698 mpcszName);
699 if (cchVendor >= 0)
700 {
701 cchModel = RTLinuxSysFsReadStrFile(szModel, sizeof(szModel),
702 "block/%s/device/model",
703 mpcszName);
704 if (cchModel >= 0)
705 {
706 misValid = true;
707 dvdCreateDeviceStrings(szVendor, szModel,
708 mszDesc, sizeof(mszDesc),
709 mszUdi, sizeof(mszUdi));
710 return;
711 }
712 }
713 }
714 if (!noProbe())
715 probeAndInitForDVD();
716 }
717
718 /** Try to find out whether a device is a DVD drive by sending it an
719 * SCSI INQUIRY command. If it is, initialise the string and validity
720 * data members for the object based on the returned data.
721 */
722 void probeAndInitForDVD()
723 {
724 AssertReturnVoid(mszNode[0] != '\0');
725 uint8_t u8Type = 0;
726 char szVendor[128] = "";
727 char szModel[128] = "";
728 int rc = cdromDoInquiry(mszNode, &u8Type, szVendor,
729 sizeof(szVendor), szModel,
730 sizeof(szModel));
731 if (RT_SUCCESS(rc) && (u8Type == TYPE_ROM))
732 {
733 misValid = true;
734 dvdCreateDeviceStrings(szVendor, szModel, mszDesc, sizeof(mszDesc),
735 mszUdi, sizeof(mszUdi));
736 }
737 }
738
739 /** Check whether the sysfs block entry is valid for a floppy device and
740 * initialise the string data members for the object. Since we only
741 * support floppies using the basic "floppy" driver, we check the driver
742 * using the entry name and a driver-specific ioctl. */
743 void validateAndInitForFloppy()
744 {
745 bool haveName = false;
746 floppy_drive_name szName;
747 char szDriver[8];
748 if ( mpcszName[0] != 'f'
749 || mpcszName[1] != 'd'
750 || mpcszName[2] < '0'
751 || mpcszName[2] > '7'
752 || mpcszName[3] != '\0')
753 return;
754 if (!noProbe())
755 haveName = floppyGetName(mszNode, mpcszName[2] - '0', szName);
756 if (RTLinuxSysFsGetLinkDest(szDriver, sizeof(szDriver), "block/%s/%s",
757 mpcszName, "device/driver") >= 0)
758 {
759 if (RTStrCmp(szDriver, "floppy"))
760 return;
761 }
762 else if (!haveName)
763 return;
764 floppyCreateDeviceStrings(haveName ? szName : NULL,
765 mpcszName[2] - '0', mszDesc,
766 sizeof(mszDesc), mszUdi, sizeof(mszUdi));
767 misValid = true;
768 }
769
770public:
771 bool isConsistent()
772 {
773 return misConsistent;
774 }
775 bool isValid()
776 {
777 return misValid;
778 }
779 const char *getDesc()
780 {
781 return mszDesc;
782 }
783 const char *getUdi()
784 {
785 return mszUdi;
786 }
787 const char *getNode()
788 {
789 return mszNode;
790 }
791};
792
793/**
794 * Helper function to query the sysfs subsystem for information about DVD
795 * drives attached to the system.
796 * @returns iprt status code
797 * @param pList where to add information about the drives detected
798 * @param isDVD are we looking for DVDs or floppies?
799 * @param pfSuccess Did we find anything?
800 *
801 * @returns IPRT status code
802 */
803/* static */
804int getDriveInfoFromSysfs(DriveInfoList *pList, bool isDVD, bool *pfSuccess)
805{
806 AssertPtrReturn(pList, VERR_INVALID_POINTER);
807 AssertPtrNullReturn(pfSuccess, VERR_INVALID_POINTER); /* Valid or Null */
808 LogFlowFunc (("pList=%p, isDVD=%u, pfSuccess=%p\n",
809 pList, (unsigned) isDVD, pfSuccess));
810 PRTDIR pDir = NULL;
811 int rc;
812 bool fSuccess = false;
813 unsigned cFound = 0;
814
815 if (!RTPathExists("/sys"))
816 return VINF_SUCCESS;
817 rc = RTDirOpen(&pDir, "/sys/block");
818 /* This might mean that sysfs semantics have changed */
819 AssertReturn(rc != VERR_FILE_NOT_FOUND, VINF_SUCCESS);
820 fSuccess = true;
821 if (RT_SUCCESS(rc))
822 for (;;)
823 {
824 RTDIRENTRY entry;
825 rc = RTDirRead(pDir, &entry, NULL);
826 Assert(rc != VERR_BUFFER_OVERFLOW); /* Should never happen... */
827 if (RT_FAILURE(rc)) /* Including overflow and no more files */
828 break;
829 if (entry.szName[0] == '.')
830 continue;
831 sysfsBlockDev dev(entry.szName, isDVD);
832 /* This might mean that sysfs semantics have changed */
833 AssertBreakStmt(dev.isConsistent(), fSuccess = false);
834 if (!dev.isValid())
835 continue;
836 try
837 {
838 pList->push_back(DriveInfo(dev.getNode(), dev.getUdi(),
839 dev.getDesc()));
840 }
841 catch(std::bad_alloc &e)
842 {
843 rc = VERR_NO_MEMORY;
844 break;
845 }
846 ++cFound;
847 }
848 RTDirClose(pDir);
849 if (rc == VERR_NO_MORE_FILES)
850 rc = VINF_SUCCESS;
851 if (RT_FAILURE(rc))
852 /* Clean up again */
853 for (unsigned i = 0; i < cFound; ++i)
854 pList->pop_back();
855 if (pfSuccess)
856 *pfSuccess = fSuccess;
857 LogFlow (("rc=%Rrc, fSuccess=%u\n", rc, (unsigned) fSuccess));
858 return rc;
859}
860
861
862/** Structure for holding information about a drive we have found */
863struct deviceNodeInfo
864{
865 /** The device number */
866 dev_t Device;
867 /** The device node path */
868 char szPath[RTPATH_MAX];
869 /** The device description */
870 char szDesc[256];
871 /** The device UDI */
872 char szUdi[256];
873};
874
875/** The maximum number of devices we will search for. */
876enum { MAX_DEVICE_NODES = 8 };
877/** An array of MAX_DEVICE_NODES devices */
878typedef struct deviceNodeInfo deviceNodeArray[MAX_DEVICE_NODES];
879
880/**
881 * Recursive worker function to walk the /dev tree looking for DVD or floppy
882 * devices.
883 * @returns true if we have already found MAX_DEVICE_NODES devices, false
884 * otherwise
885 * @param pszPath the path to start recursing. The function can modify
886 * this string at and after the terminating zero
887 * @param cchPath the size of the buffer (not the string!) in @a pszPath
888 * @param aDevices where to fill in information about devices that we have
889 * found
890 * @param wantDVD are we looking for DVD devices (or floppies)?
891 */
892static bool devFindDeviceRecursive(char *pszPath, size_t cchPath,
893 deviceNodeArray aDevices, bool wantDVD)
894{
895 /*
896 * Check assumptions made by the code below.
897 */
898 size_t const cchBasePath = strlen(pszPath);
899 AssertReturn(cchBasePath < RTPATH_MAX - 10U, false);
900 AssertReturn(pszPath[cchBasePath - 1] != '/', false);
901
902 PRTDIR pDir;
903 if (RT_FAILURE(RTDirOpen(&pDir, pszPath)))
904 return false;
905 for (;;)
906 {
907 RTDIRENTRY Entry;
908 RTFSOBJINFO ObjInfo;
909 int rc = RTDirRead(pDir, &Entry, NULL);
910 if (RT_FAILURE(rc))
911 break;
912 if (Entry.enmType == RTDIRENTRYTYPE_UNKNOWN)
913 {
914 if (RT_FAILURE(RTPathQueryInfo(pszPath, &ObjInfo,
915 RTFSOBJATTRADD_UNIX)))
916 continue;
917 if (RTFS_IS_SYMLINK(ObjInfo.Attr.fMode))
918 continue;
919 }
920
921 if (Entry.enmType == RTDIRENTRYTYPE_SYMLINK)
922 continue;
923 pszPath[cchBasePath] = '\0';
924 if (RT_FAILURE(RTPathAppend(pszPath, cchPath, Entry.szName)))
925 break;
926
927 /* Do the matching. */
928 dev_t DevNode;
929 char szDesc[256], szUdi[256];
930 if (!devValidateDevice(pszPath, wantDVD, &DevNode, szDesc,
931 sizeof(szDesc), szUdi, sizeof(szUdi)))
932 continue;
933 unsigned i;
934 for (i = 0; i < MAX_DEVICE_NODES; ++i)
935 if (!aDevices[i].Device || (aDevices[i].Device == DevNode))
936 break;
937 AssertBreak(i < MAX_DEVICE_NODES);
938 if (aDevices[i].Device)
939 continue;
940 aDevices[i].Device = DevNode;
941 RTStrPrintf(aDevices[i].szPath, sizeof(aDevices[i].szPath),
942 "%s", pszPath);
943 AssertCompile(sizeof(aDevices[i].szDesc) == sizeof(szDesc));
944 strcpy(aDevices[i].szDesc, szDesc);
945 AssertCompile(sizeof(aDevices[i].szUdi) == sizeof(szUdi));
946 strcpy(aDevices[i].szUdi, szUdi);
947 if (i == MAX_DEVICE_NODES - 1)
948 break;
949 continue;
950
951 /* Recurse into subdirectories. */
952 if ( (Entry.enmType == RTDIRENTRYTYPE_UNKNOWN)
953 && !RTFS_IS_DIRECTORY(ObjInfo.Attr.fMode))
954 continue;
955 if (Entry.enmType != RTDIRENTRYTYPE_DIRECTORY)
956 continue;
957 if (Entry.szName[0] == '.')
958 continue;
959
960 if (devFindDeviceRecursive(pszPath, cchPath, aDevices, wantDVD))
961 break;
962 }
963 RTDirClose(pDir);
964 return aDevices[MAX_DEVICE_NODES - 1].Device ? true : false;
965}
966
967
968/**
969 * Recursively walk through the /dev tree and add any DVD or floppy drives we
970 * find and can access to our list. (If we can't access them we can't check
971 * whether or not they are really DVD or floppy drives).
972 * @note this is rather slow (a couple of seconds) for DVD probing on
973 * systems with a static /dev tree, as the current code tries to open
974 * any device node with a major/minor combination that could belong to
975 * a CD-ROM device, and opening a non-existent device can take a non.
976 * negligeable time on Linux. If it is ever necessary to improve this
977 * (static /dev trees are no longer very fashionable these days, and
978 * sysfs looks like it will be with us for a while), we could further
979 * reduce the number of device nodes we open by checking whether the
980 * driver is actually loaded in /proc/devices, and by counting the
981 * of currently attached SCSI CD-ROM devices in /proc/scsi/scsi (yes,
982 * there is a race, but it is probably not important for us).
983 * @returns iprt status code
984 * @param pList the list to append the drives found to
985 * @param isDVD are we looking for DVD drives or for floppies?
986 * @param pfSuccess this will be set to true if we found at least one drive
987 * and to false otherwise. Optional.
988 */
989/* static */
990int getDriveInfoFromDev(DriveInfoList *pList, bool isDVD, bool *pfSuccess)
991{
992 AssertPtrReturn(pList, VERR_INVALID_POINTER);
993 AssertPtrNullReturn(pfSuccess, VERR_INVALID_POINTER);
994 LogFlowFunc(("pList=%p, isDVD=%d, pfSuccess=%p\n", pList, isDVD,
995 pfSuccess));
996 int rc = VINF_SUCCESS;
997 bool success = false;
998
999 char szPath[RTPATH_MAX] = "/dev";
1000 deviceNodeArray aDevices;
1001 RT_ZERO(aDevices);
1002 devFindDeviceRecursive(szPath, sizeof(szPath), aDevices, isDVD);
1003 try
1004 {
1005 for (unsigned i = 0; i < MAX_DEVICE_NODES; ++i)
1006 {
1007 if (aDevices[i].Device)
1008 {
1009 pList->push_back(DriveInfo(aDevices[i].szPath,
1010 aDevices[i].szUdi, aDevices[i].szDesc));
1011 success = true;
1012 }
1013 }
1014 if (pfSuccess != NULL)
1015 *pfSuccess = success;
1016 }
1017 catch(std::bad_alloc &e)
1018 {
1019 rc = VERR_NO_MEMORY;
1020 }
1021 LogFlowFunc (("rc=%Rrc, success=%d\n", rc, success));
1022 return rc;
1023}
1024
1025
1026int VBoxMainUSBDeviceInfo::UpdateDevices ()
1027{
1028 LogFlowThisFunc(("entered\n"));
1029 int rc = VINF_SUCCESS;
1030 bool success = false; /* Have we succeeded in finding anything yet? */
1031 try
1032 {
1033 bool halSuccess = false;
1034 mDeviceList.clear();
1035#if defined(RT_OS_LINUX)
1036#ifdef VBOX_WITH_DBUS
1037 if ( RT_SUCCESS(rc)
1038 && RT_SUCCESS(RTDBusLoadLib())
1039 && (!success || testing()))
1040 rc = getUSBDeviceInfoFromHal(&mDeviceList, &halSuccess);
1041 /* Try the old API if the new one *succeeded* as only one of them will
1042 * pick up devices anyway. */
1043 if (RT_SUCCESS(rc) && halSuccess && (!success || testing()))
1044 rc = getOldUSBDeviceInfoFromHal(&mDeviceList, &halSuccess);
1045 if (!success)
1046 success = halSuccess;
1047#endif /* VBOX_WITH_DBUS defined */
1048 if ( RT_SUCCESS(rc)
1049 && (!success || testing()))
1050 rc = getUSBDeviceInfoFromSysfs(&mDeviceList, &halSuccess);
1051#endif /* RT_OS_LINUX */
1052 }
1053 catch(std::bad_alloc &e)
1054 {
1055 rc = VERR_NO_MEMORY;
1056 }
1057 LogFlowThisFunc(("rc=%Rrc\n", rc));
1058 return rc;
1059}
1060
1061#if defined RT_OS_LINUX && defined VBOX_WITH_DBUS
1062class hotplugDBusImpl : public VBoxMainHotplugWaiterImpl
1063{
1064 /** The connection to DBus */
1065 RTMemAutoPtr <DBusConnection, VBoxHalShutdownPrivate> mConnection;
1066 /** Semaphore which is set when a device is hotplugged and reset when
1067 * it is read. */
1068 volatile bool mTriggered;
1069 /** A flag to say that we wish to interrupt the current wait. */
1070 volatile bool mInterrupt;
1071
1072public:
1073 /** Test whether this implementation can be used on the current system */
1074 static bool HalAvailable(void)
1075 {
1076 RTMemAutoPtr<DBusConnection, VBoxHalShutdown> dbusConnection;
1077
1078 /* Try to open a test connection to hal */
1079 if (RT_SUCCESS(RTDBusLoadLib()) && RT_SUCCESS(halInit (&dbusConnection)))
1080 return !!dbusConnection;
1081 return false;
1082 }
1083
1084 /** Constructor */
1085 hotplugDBusImpl (void);
1086 virtual ~hotplugDBusImpl (void);
1087 /** @copydoc VBoxMainHotplugWaiter::Wait */
1088 virtual int Wait (RTMSINTERVAL cMillies);
1089 /** @copydoc VBoxMainHotplugWaiter::Interrupt */
1090 virtual void Interrupt (void);
1091};
1092
1093/* This constructor sets up a private connection to the DBus daemon, connects
1094 * to the hal service and installs a filter which sets the mTriggered flag in
1095 * the Context structure when a device (not necessarily USB) is added or
1096 * removed. */
1097hotplugDBusImpl::hotplugDBusImpl (void) : mTriggered(false), mInterrupt(false)
1098{
1099 int rc = VINF_SUCCESS;
1100
1101 if (RT_SUCCESS(RTDBusLoadLib()))
1102 {
1103 for (unsigned i = 0; RT_SUCCESS(rc) && i < 5 && !mConnection; ++i)
1104 {
1105 rc = halInitPrivate (&mConnection);
1106 }
1107 if (!mConnection)
1108 rc = VERR_NOT_SUPPORTED;
1109 DBusMessage *pMessage;
1110 while ( RT_SUCCESS(rc)
1111 && (pMessage = dbus_connection_pop_message (mConnection.get())) != NULL)
1112 dbus_message_unref (pMessage); /* empty the message queue. */
1113 if ( RT_SUCCESS(rc)
1114 && !dbus_connection_add_filter (mConnection.get(),
1115 dbusFilterFunction,
1116 (void *) &mTriggered, NULL))
1117 rc = VERR_NO_MEMORY;
1118 if (RT_FAILURE(rc))
1119 mConnection.reset();
1120 }
1121}
1122
1123/* Destructor */
1124hotplugDBusImpl::~hotplugDBusImpl ()
1125{
1126 if (!!mConnection)
1127 dbus_connection_remove_filter (mConnection.get(), dbusFilterFunction,
1128 (void *) &mTriggered);
1129}
1130
1131/* Currently this is implemented using a timed out wait on our private DBus
1132 * connection. Because the connection is private we don't have to worry about
1133 * blocking other users. */
1134int hotplugDBusImpl::Wait(RTMSINTERVAL cMillies)
1135{
1136 int rc = VINF_SUCCESS;
1137 if (!mConnection)
1138 rc = VERR_NOT_SUPPORTED;
1139 bool connected = true;
1140 mTriggered = false;
1141 mInterrupt = false;
1142 unsigned cRealMillies;
1143 if (cMillies != RT_INDEFINITE_WAIT)
1144 cRealMillies = cMillies;
1145 else
1146 cRealMillies = DBUS_POLL_TIMEOUT;
1147 while ( RT_SUCCESS(rc) && connected && !mTriggered
1148 && !mInterrupt)
1149 {
1150 connected = dbus_connection_read_write_dispatch (mConnection.get(),
1151 cRealMillies);
1152 if (mInterrupt)
1153 LogFlowFunc(("wait loop interrupted\n"));
1154 if (cMillies != RT_INDEFINITE_WAIT)
1155 mInterrupt = true;
1156 }
1157 if (!connected)
1158 rc = VERR_TRY_AGAIN;
1159 return rc;
1160}
1161
1162/* Set a flag to tell the Wait not to resume next time it times out. */
1163void hotplugDBusImpl::Interrupt()
1164{
1165 LogFlowFunc(("\n"));
1166 mInterrupt = true;
1167}
1168#endif /* !(defined RT_OS_LINUX && defined VBOX_WITH_DBUS) */
1169
1170class hotplugNullImpl : public VBoxMainHotplugWaiterImpl
1171{
1172public:
1173 hotplugNullImpl (void) {}
1174 virtual ~hotplugNullImpl (void) {}
1175 /** @copydoc VBoxMainHotplugWaiter::Wait */
1176 virtual int Wait (RTMSINTERVAL)
1177 {
1178 return VERR_NOT_SUPPORTED;
1179 }
1180 /** @copydoc VBoxMainHotplugWaiter::Interrupt */
1181 virtual void Interrupt (void) {}
1182};
1183
1184#ifdef VBOX_USB_WITH_SYSFS
1185
1186/** @todo move these elsewhere, but wait for words of wisdom from the iprt
1187 * maintainer first :)
1188 * The ideas behind the macros are on the one hand to increase code readability
1189 * by reducing the space taken by the error handling (exceptions using pure C
1190 * if you like, where a do {} while(0) block replaces the try {} catch() and a
1191 * variable at a higher scope remembers the success or failure); and on the
1192 * other to reduce duplication in error handling code, hopefully thereby
1193 * raising the chances that error paths will actually work.
1194 */
1195
1196/**
1197 * Store an iprt status code (expected to be a function call with the status
1198 * code as its return value) in a variable and execute a break statement if
1199 * the status is unsuccessful.
1200 * @param rc where to store the value
1201 * @param expr the expression returning the status. @a expr will only be
1202 * evaluated once by the macro call
1203 */
1204#define SETRCBREAK(rc, expr) \
1205 if (RT_FAILURE(((rc) = (expr)))) \
1206 break; \
1207 else do {} while (0)
1208
1209/**
1210 * Store an iprt status code (expected to be a function call with the status
1211 * code as its return value) in a variable and execute a return statement if
1212 * the status is unsuccessful.
1213 * @param rc where to store the value
1214 * @param expr the expression returning the status. @a expr will only be
1215 * evaluated once by the macro call
1216 */
1217#define SETRCRETURN(rc, expr) \
1218 do \
1219 { \
1220 (rc) = (expr); \
1221 if (RT_FAILURE(rc)) \
1222 return (rc); \
1223 } while (0)
1224
1225static void testSetRCBreak(void)
1226{
1227 int rc = VINF_SUCCESS;
1228 do {
1229 SETRCBREAK(rc, VERR_WRONG_ORDER);
1230 rc = VINF_SUCCESS;
1231 } while(0);
1232 Assert(rc == VERR_WRONG_ORDER);
1233 rc = VERR_GENERAL_FAILURE;
1234 do {
1235 SETRCBREAK(rc, VINF_BUFFER_OVERFLOW);
1236 Assert(rc == VINF_BUFFER_OVERFLOW);
1237 rc = VERR_WRONG_ORDER;
1238 } while(0);
1239 Assert(rc == VERR_WRONG_ORDER);
1240}
1241
1242static int testSetRCReturnWorker(int rc)
1243{
1244 int rc2 = VERR_WRONG_ORDER;
1245 SETRCRETURN(rc2, rc);
1246 Assert(rc == rc2);
1247 return VINF_SUCCESS;
1248}
1249
1250static void testSetRCReturn(void)
1251{
1252 Assert(testSetRCReturnWorker(VERR_GENERAL_FAILURE) == VERR_GENERAL_FAILURE);
1253 AssertRCSuccess(testSetRCReturnWorker(VINF_BUFFER_OVERFLOW));
1254}
1255
1256#define SYSFS_USB_DEVICE_PATH "/dev/bus/usb"
1257#define SYSFS_WAKEUP_STRING "Wake up!"
1258
1259class hotplugSysfsFAMImpl : public VBoxMainHotplugWaiterImpl
1260{
1261 /** Pipe used to interrupt wait(), the read end. */
1262 RTPIPE mhWakeupPipeR;
1263 /** Pipe used to interrupt wait(), the write end. */
1264 RTPIPE mhWakeupPipeW;
1265 /** Our connection to FAM for polling for changes on sysfs. */
1266 FAMConnection mFAMConnection;
1267 /** Has our connection been initialised? */
1268 bool mfFAMInitialised;
1269 /** The iprt native handle of the FAM fd socket. */
1270 RTSOCKET mhFAMFD;
1271 /** Poll set containing the FAM socket and the termination pipe */
1272 RTPOLLSET mhPollSet;
1273 /** iprt result code from object initialisation. Should be AssertReturn-ed
1274 * on at the start of all methods. I went this way because I didn't want
1275 * to deal with exceptions. */
1276 int mStatus;
1277 /** ID values associates with the wakeup pipe and the FAM socket for polling
1278 */
1279 enum
1280 {
1281 RPIPE_ID = 1,
1282 FAMFD_ID
1283 };
1284
1285 /** Clean up any resources in use, gracefully skipping over any which have
1286 * not yet been allocated or already cleaned up. Intended to be called
1287 * from the destructor or after a failed initialisation. */
1288 void term(void);
1289
1290 /** Open our connection to FAM and convert the status to iprt.
1291 * @todo really convert the status
1292 */
1293 int openFAM(void)
1294 {
1295 if (FAMOpen(&mFAMConnection) < 0)
1296 return VERR_UNRESOLVED_ERROR; /* VERR_FAM_OPEN_FAILED */
1297 mfFAMInitialised = true;
1298 return VINF_SUCCESS;
1299 }
1300
1301 /** Monitor a file through the FAM connection. */
1302 int monitorDirectoryFAM(const char *pszName)
1303 {
1304 AssertReturn(mfFAMInitialised, VERR_WRONG_ORDER);
1305 FAMRequest dummyReq;
1306 if (FAMMonitorDirectory(&mFAMConnection, pszName, &dummyReq, NULL) < 0)
1307 return VERR_UNRESOLVED_ERROR; /* VERR_FAM_MONITOR_FILE_FAILED */
1308 return VINF_SUCCESS;
1309 }
1310
1311 /** Quick failure test of the monitor function - we temporarily invalidate
1312 * the connection FD to trigger an error path. */
1313 void testmonitorDirectoryFAM(void)
1314 {
1315 int oldFD = FAMCONNECTION_GETFD(&mFAMConnection);
1316 FAMCONNECTION_GETFD(&mFAMConnection) = -1;
1317 Assert(monitorDirectoryFAM(NULL) == VERR_UNRESOLVED_ERROR);
1318 FAMCONNECTION_GETFD(&mFAMConnection) = oldFD;
1319 }
1320
1321 /** Close our connection to FAM. We ignore errors as there is no
1322 * documentation as to what they mean, and the only error which might
1323 * interest us (EINTR) should be (but isn't) handled inside the library. */
1324 void closeFAM(void)
1325 {
1326 if (mfFAMInitialised)
1327 FAMClose(&mFAMConnection);
1328 mfFAMInitialised = false;
1329 }
1330public:
1331 hotplugSysfsFAMImpl(void);
1332 virtual ~hotplugSysfsFAMImpl(void)
1333 {
1334 term();
1335#ifdef DEBUG
1336 /** The first call to term should mark all resources as freed, so this
1337 * should be a semantic no-op. */
1338 term();
1339#endif
1340 }
1341 /** Is sysfs available on this system? If so we expect that this
1342 * implementation will be usable. */
1343 static bool SysfsAvailable(void)
1344 {
1345 return RTDirExists(SYSFS_USB_DEVICE_PATH);
1346 }
1347 /** @copydoc VBoxMainHotplugWaiter::Wait */
1348 virtual int Wait(RTMSINTERVAL);
1349 /** @copydoc VBoxMainHotplugWaiter::Interrupt */
1350 virtual void Interrupt(void);
1351};
1352
1353hotplugSysfsFAMImpl::hotplugSysfsFAMImpl(void) :
1354 mhWakeupPipeR(NIL_RTPIPE), mhWakeupPipeW(NIL_RTPIPE),
1355 mfFAMInitialised(false), mhFAMFD(NIL_RTSOCKET), mhPollSet(NIL_RTPOLLSET),
1356 mStatus(VERR_WRONG_ORDER)
1357{
1358#ifdef DEBUG
1359 /* Excercise the code path (term() on a not-fully-initialised object) as
1360 * well as we can. On an uninitialised object this method is a sematic
1361 * no-op. */
1362 term();
1363 /** Unit test our macros above */
1364 testSetRCBreak();
1365 testSetRCReturn();
1366#endif
1367 int rc;
1368 do {
1369 SETRCBREAK(rc, RTPipeCreate(&mhWakeupPipeR, &mhWakeupPipeW, 0));
1370 SETRCBREAK(rc, openFAM());
1371 SETRCBREAK(rc, RTSocketFromNative
1372 (&mhFAMFD, FAMCONNECTION_GETFD(&mFAMConnection)));
1373 SETRCBREAK(rc, monitorDirectoryFAM(SYSFS_USB_DEVICE_PATH));
1374 SETRCBREAK(rc, RTPollSetCreate(&mhPollSet));
1375 SETRCBREAK(rc, RTPollSetAddSocket
1376 (mhPollSet, mhFAMFD, RTPOLL_EVT_READ, FAMFD_ID));
1377 SETRCBREAK(rc, RTPollSetAddPipe
1378 (mhPollSet, mhWakeupPipeR, RTPOLL_EVT_READ, RPIPE_ID));
1379#ifdef DEBUG
1380 /** Other tests */
1381 testmonitorDirectoryFAM();
1382#endif
1383 } while(0);
1384 mStatus = rc;
1385 if (RT_FAILURE(rc))
1386 term();
1387}
1388
1389void hotplugSysfsFAMImpl::term(void)
1390{
1391 RTPipeClose(mhWakeupPipeR);
1392 mhWakeupPipeR = NIL_RTPIPE;
1393 RTPipeClose(mhWakeupPipeW);
1394 mhWakeupPipeW = NIL_RTPIPE;
1395 closeFAM();
1396 mhFAMFD = NIL_RTSOCKET;
1397 RTPollSetDestroy(mhPollSet);
1398 mhPollSet = NIL_RTPOLLSET;
1399}
1400
1401int hotplugSysfsFAMImpl::Wait(RTMSINTERVAL aMillies)
1402{
1403 uint32_t id;
1404 int rc;
1405 FAMEvent ev;
1406
1407 if (RT_FAILURE(mStatus))
1408 return VERR_NOT_SUPPORTED;
1409 /* timeout returns */
1410 SETRCRETURN(rc, RTPoll(mhPollSet, aMillies, NULL, &id));
1411 if (id == RPIPE_ID)
1412 {
1413 /* drain the pipe */
1414 char szBuf[sizeof(SYSFS_WAKEUP_STRING)];
1415 rc = RTPipeRead(mhWakeupPipeR, szBuf, sizeof(szBuf), NULL);
1416 AssertRC(rc);
1417 return VINF_SUCCESS;
1418 }
1419 AssertReturn(id == FAMFD_ID, VERR_NOT_SUPPORTED);
1420 /* Samba re-opens the connection to FAM if this happens. */
1421 AssertReturn(FAMNextEvent(&mFAMConnection, &ev) == 1,
1422 VERR_NOT_SUPPORTED);
1423 switch(ev.code)
1424 {
1425 case FAMExists:
1426 case FAMEndExist:
1427 return VERR_TRY_AGAIN;
1428 default:
1429 break;
1430 }
1431 return VINF_SUCCESS;
1432}
1433
1434void hotplugSysfsFAMImpl::Interrupt(void)
1435{
1436 size_t cbDummy;
1437 int rc = RTPipeWrite(mhWakeupPipeW, SYSFS_WAKEUP_STRING,
1438 sizeof(SYSFS_WAKEUP_STRING), &cbDummy);
1439 if (RT_SUCCESS(rc))
1440 RTPipeFlush(mhWakeupPipeW);
1441}
1442
1443#endif /* VBOX_USB_WTH_SYSFS */
1444
1445VBoxMainHotplugWaiter::VBoxMainHotplugWaiter(void)
1446{
1447#if defined RT_OS_LINUX && defined VBOX_WITH_DBUS
1448 if (hotplugDBusImpl::HalAvailable())
1449 {
1450 mImpl = new hotplugDBusImpl;
1451 return;
1452 }
1453#endif /* !(defined RT_OS_LINUX && defined VBOX_WITH_DBUS) */
1454#if defined VBOX_USB_WITH_SYSFS
1455 if (hotplugSysfsFAMImpl::SysfsAvailable())
1456 {
1457 mImpl = new hotplugSysfsFAMImpl;
1458 return;
1459 }
1460#endif /* !(defined RT_OS_LINUX && defined VBOX_WITH_DBUS) */
1461 mImpl = new hotplugNullImpl;
1462}
1463
1464class sysfsPathHandler
1465{
1466 /** Called on each element of the sysfs directory. Can e.g. store
1467 * interesting entries in a list. */
1468 virtual bool handle(const char *pcszNode) = 0;
1469public:
1470 bool doHandle(const char *pcszNode)
1471 {
1472 AssertPtr(pcszNode);
1473 Assert(pcszNode[0] == '/');
1474 Assert(RTPathExists(pcszNode));
1475 return handle(pcszNode);
1476 }
1477};
1478
1479/**
1480 * Helper function to walk a sysfs directory for extracting information about
1481 * devices.
1482 * @returns iprt status code
1483 * @param pcszPath Sysfs directory to walk. Must exist.
1484 * @param pHandler Handler object which will be invoked on each directory
1485 * entry
1486 *
1487 * @returns IPRT status code
1488 */
1489/* static */
1490int getDeviceInfoFromSysfs(const char *pcszPath, sysfsPathHandler *pHandler)
1491{
1492 AssertPtrReturn(pcszPath, VERR_INVALID_POINTER);
1493 AssertPtrReturn(pHandler, VERR_INVALID_POINTER);
1494 LogFlowFunc (("pcszPath=%s, pHandler=%p\n", pcszPath, pHandler));
1495 PRTDIR pDir = NULL;
1496 int rc;
1497
1498 rc = RTDirOpen(&pDir, pcszPath);
1499 AssertRCReturn(rc, rc);
1500 while (RT_SUCCESS(rc))
1501 {
1502 RTDIRENTRY entry;
1503 char szPath[RTPATH_MAX], szAbsPath[RTPATH_MAX];
1504
1505 rc = RTDirRead(pDir, &entry, NULL);
1506 Assert(rc != VERR_BUFFER_OVERFLOW); /* Should never happen... */
1507 /* We break on "no more files" as well as on "real" errors */
1508 if (RT_FAILURE(rc))
1509 break;
1510 if (entry.szName[0] == '.')
1511 continue;
1512 if (RTStrPrintf(szPath, sizeof(szPath), "%s/%s", pcszPath,
1513 entry.szName) >= sizeof(szPath))
1514 rc = VERR_BUFFER_OVERFLOW;
1515 if (RT_FAILURE(rc))
1516 break;
1517 rc = RTPathReal(szPath, szAbsPath, sizeof(szAbsPath));
1518 AssertRCBreak(rc); /* sysfs should guarantee that this exists */
1519 if (!pHandler->doHandle(szAbsPath))
1520 break;
1521 }
1522 RTDirClose(pDir);
1523 if (rc == VERR_NO_MORE_FILES)
1524 rc = VINF_SUCCESS;
1525 LogFlow (("rc=%Rrc\n", rc));
1526 return rc;
1527}
1528
1529
1530/**
1531 * Tell whether a file in /sys/bus/usb/devices is a device rather than an
1532 * interface. To be used with getDeviceInfoFromSysfs().
1533 */
1534class matchUSBDevice : public sysfsPathHandler
1535{
1536 USBDeviceInfoList *mList;
1537public:
1538 matchUSBDevice(USBDeviceInfoList *pList) : mList(pList) {}
1539private:
1540 virtual bool handle(const char *pcszNode)
1541 {
1542 const char *pcszFile = strrchr(pcszNode, '/');
1543 if (strchr(pcszFile, ':'))
1544 return true;
1545 dev_t devnum = RTLinuxSysFsReadDevNumFile("%s/dev", pcszNode);
1546 AssertReturn (devnum, true);
1547 char szDevPath[RTPATH_MAX];
1548 ssize_t cchDevPath;
1549 cchDevPath = RTLinuxFindDevicePath(devnum, RTFS_TYPE_DEV_CHAR,
1550 szDevPath, sizeof(szDevPath),
1551 "/dev/bus/usb/%.3d/%.3d",
1552 RTLinuxSysFsReadIntFile(10, "%s/busnum", pcszNode),
1553 RTLinuxSysFsReadIntFile(10, "%s/devnum", pcszNode));
1554 if (cchDevPath < 0)
1555 return true;
1556 try
1557 {
1558 mList->push_back(USBDeviceInfo(szDevPath, pcszNode));
1559 }
1560 catch(std::bad_alloc &e)
1561 {
1562 return false;
1563 }
1564 return true;
1565 }
1566};
1567
1568/**
1569 * Tell whether a file in /sys/bus/usb/devices is an interface rather than a
1570 * device. To be used with getDeviceInfoFromSysfs().
1571 */
1572class matchUSBInterface : public sysfsPathHandler
1573{
1574 USBDeviceInfo *mInfo;
1575public:
1576 /** This constructor is currently used to unit test the class logic in
1577 * debug builds. Since no access is made to anything outside the class,
1578 * this shouldn't cause any slowdown worth mentioning. */
1579 matchUSBInterface(USBDeviceInfo *pInfo) : mInfo(pInfo)
1580 {
1581 Assert(isAnInterfaceOf("/sys/devices/pci0000:00/0000:00:1a.0/usb3/3-0:1.0",
1582 "/sys/devices/pci0000:00/0000:00:1a.0/usb3"));
1583 Assert(!isAnInterfaceOf("/sys/devices/pci0000:00/0000:00:1a.0/usb3/3-1",
1584 "/sys/devices/pci0000:00/0000:00:1a.0/usb3"));
1585 Assert(!isAnInterfaceOf("/sys/devices/pci0000:00/0000:00:1a.0/usb3/3-0:1.0/driver",
1586 "/sys/devices/pci0000:00/0000:00:1a.0/usb3"));
1587 }
1588private:
1589 /** The logic for testing whether a sysfs address corresponds to an
1590 * interface of a device. Both must be referenced by their canonical
1591 * sysfs paths. This is not tested, as the test requires file-system
1592 * interaction. */
1593 bool isAnInterfaceOf(const char *pcszIface, const char *pcszDev)
1594 {
1595 size_t cchDev = strlen(pcszDev);
1596
1597 AssertPtr(pcszIface);
1598 AssertPtr(pcszDev);
1599 Assert(pcszIface[0] == '/');
1600 Assert(pcszDev[0] == '/');
1601 Assert(pcszDev[cchDev - 1] != '/');
1602 /* If this passes, pcszIface is at least cchDev long */
1603 if (strncmp(pcszIface, pcszDev, cchDev))
1604 return false;
1605 /* If this passes, pcszIface is longer than cchDev */
1606 if (pcszIface[cchDev] != '/')
1607 return false;
1608 /* In sysfs an interface is an immediate subdirectory of the device */
1609 if (strchr(pcszIface + cchDev + 1, '/'))
1610 return false;
1611 /* And it always has a colon in its name */
1612 if (!strchr(pcszIface + cchDev + 1, ':'))
1613 return false;
1614 /* And hopefully we have now elimitated everything else */
1615 return true;
1616 }
1617
1618 virtual bool handle(const char *pcszNode)
1619 {
1620 if (!isAnInterfaceOf(pcszNode, mInfo->mSysfsPath.c_str()))
1621 return true;
1622 try
1623 {
1624 mInfo->mInterfaces.push_back(pcszNode);
1625 }
1626 catch(std::bad_alloc &e)
1627 {
1628 return false;
1629 }
1630 return true;
1631 }
1632};
1633
1634/**
1635 * Helper function to query the sysfs subsystem for information about USB
1636 * devices attached to the system.
1637 * @returns iprt status code
1638 * @param pList where to add information about the drives detected
1639 * @param pfSuccess Did we find anything?
1640 *
1641 * @returns IPRT status code
1642 */
1643static int getUSBDeviceInfoFromSysfs(USBDeviceInfoList *pList,
1644 bool *pfSuccess)
1645{
1646 AssertPtrReturn(pList, VERR_INVALID_POINTER);
1647 AssertPtrNullReturn(pfSuccess, VERR_INVALID_POINTER); /* Valid or Null */
1648 LogFlowFunc (("pList=%p, pfSuccess=%p\n",
1649 pList, pfSuccess));
1650 size_t cDevices = pList->size();
1651 matchUSBDevice devHandler(pList);
1652 int rc = getDeviceInfoFromSysfs("/sys/bus/usb/devices", &devHandler);
1653 do {
1654 if (RT_FAILURE(rc))
1655 break;
1656 for (USBDeviceInfoList::iterator pInfo = pList->begin();
1657 pInfo != pList->end(); ++pInfo)
1658 {
1659 matchUSBInterface ifaceHandler(&*pInfo);
1660 rc = getDeviceInfoFromSysfs("/sys/bus/usb/devices", &ifaceHandler);
1661 if (RT_FAILURE(rc))
1662 break;
1663 }
1664 } while(0);
1665 if (RT_FAILURE(rc))
1666 /* Clean up again */
1667 while (pList->size() > cDevices)
1668 pList->pop_back();
1669 if (pfSuccess)
1670 *pfSuccess = RT_SUCCESS(rc);
1671 LogFlow (("rc=%Rrc\n", rc));
1672 return rc;
1673}
1674
1675
1676#if defined(RT_OS_LINUX) && defined(VBOX_WITH_DBUS)
1677/** Wrapper class around DBusError for automatic cleanup */
1678class autoDBusError
1679{
1680 DBusError mError;
1681public:
1682 autoDBusError () { dbus_error_init (&mError); }
1683 ~autoDBusError ()
1684 {
1685 if (IsSet())
1686 dbus_error_free (&mError);
1687 }
1688 DBusError &get () { return mError; }
1689 bool IsSet ()
1690 {
1691 Assert((mError.name == NULL) == (mError.message == NULL));
1692 return (mError.name != NULL);
1693 }
1694 bool HasName (const char *pcszName)
1695 {
1696 Assert((mError.name == NULL) == (mError.message == NULL));
1697 return (RTStrCmp (mError.name, pcszName) == 0);
1698 }
1699 void FlowLog ()
1700 {
1701 if (IsSet ())
1702 LogFlow(("DBus error %s: %s\n", mError.name, mError.message));
1703 }
1704};
1705
1706/**
1707 * Helper function for setting up a connection to the DBus daemon and
1708 * registering with the hal service.
1709 *
1710 * @note If libdbus is being loaded at runtime then be sure to call
1711 * VBoxDBusCheckPresence before calling this.
1712 * @returns iprt status code
1713 * @param ppConnection where to store the connection handle
1714 */
1715/* static */
1716int halInit (RTMemAutoPtr <DBusConnection, VBoxHalShutdown> *pConnection)
1717{
1718 AssertReturn(VALID_PTR (pConnection), VERR_INVALID_POINTER);
1719 LogFlowFunc (("pConnection=%p\n", pConnection));
1720 int rc = VINF_SUCCESS;
1721 bool halSuccess = true;
1722 autoDBusError dbusError;
1723
1724 RTMemAutoPtr <DBusConnection, VBoxDBusConnectionUnref> dbusConnection;
1725 dbusConnection = dbus_bus_get (DBUS_BUS_SYSTEM, &dbusError.get());
1726 if (!dbusConnection)
1727 halSuccess = false;
1728 if (halSuccess)
1729 {
1730 dbus_connection_set_exit_on_disconnect (dbusConnection.get(), false);
1731 halSuccess = dbus_bus_name_has_owner (dbusConnection.get(),
1732 "org.freedesktop.Hal", &dbusError.get());
1733 }
1734 if (halSuccess)
1735 {
1736 dbus_bus_add_match (dbusConnection.get(),
1737 "type='signal',"
1738 "interface='org.freedesktop.Hal.Manager',"
1739 "sender='org.freedesktop.Hal',"
1740 "path='/org/freedesktop/Hal/Manager'",
1741 &dbusError.get());
1742 halSuccess = !dbusError.IsSet();
1743 }
1744 if (dbusError.HasName (DBUS_ERROR_NO_MEMORY))
1745 rc = VERR_NO_MEMORY;
1746 if (halSuccess)
1747 *pConnection = dbusConnection.release();
1748 LogFlowFunc(("rc=%Rrc, (*pConnection).get()=%p\n", rc, (*pConnection).get()));
1749 dbusError.FlowLog();
1750 return rc;
1751}
1752
1753/**
1754 * Helper function for setting up a private connection to the DBus daemon and
1755 * registering with the hal service. Private connections are considered
1756 * unsociable and should not be used unnecessarily (as per the DBus API docs).
1757 *
1758 * @note If libdbus is being loaded at runtime then be sure to call
1759 * VBoxDBusCheckPresence before calling this.
1760 * @returns iprt status code
1761 * @param pConnection where to store the connection handle
1762 */
1763/* static */
1764int halInitPrivate (RTMemAutoPtr <DBusConnection, VBoxHalShutdownPrivate> *pConnection)
1765{
1766 AssertReturn(VALID_PTR (pConnection), VERR_INVALID_POINTER);
1767 LogFlowFunc (("pConnection=%p\n", pConnection));
1768 int rc = VINF_SUCCESS;
1769 bool halSuccess = true;
1770 autoDBusError dbusError;
1771
1772 RTMemAutoPtr <DBusConnection, VBoxDBusConnectionCloseAndUnref> dbusConnection;
1773 dbusConnection = dbus_bus_get_private (DBUS_BUS_SYSTEM, &dbusError.get());
1774 if (!dbusConnection)
1775 halSuccess = false;
1776 if (halSuccess)
1777 {
1778 dbus_connection_set_exit_on_disconnect (dbusConnection.get(), false);
1779 halSuccess = dbus_bus_name_has_owner (dbusConnection.get(),
1780 "org.freedesktop.Hal", &dbusError.get());
1781 }
1782 if (halSuccess)
1783 {
1784 dbus_bus_add_match (dbusConnection.get(),
1785 "type='signal',"
1786 "interface='org.freedesktop.Hal.Manager',"
1787 "sender='org.freedesktop.Hal',"
1788 "path='/org/freedesktop/Hal/Manager'",
1789 &dbusError.get());
1790 halSuccess = !dbusError.IsSet();
1791 }
1792 if (dbusError.HasName (DBUS_ERROR_NO_MEMORY))
1793 rc = VERR_NO_MEMORY;
1794 if (halSuccess)
1795 *pConnection = dbusConnection.release();
1796 LogFlowFunc(("rc=%Rrc, (*pConnection).get()=%p\n", rc, (*pConnection).get()));
1797 dbusError.FlowLog();
1798 return rc;
1799}
1800
1801/**
1802 * Helper function for shutting down a connection to DBus and hal.
1803 * @param pConnection the connection handle
1804 */
1805/* extern */
1806void VBoxHalShutdown (DBusConnection *pConnection)
1807{
1808 AssertReturnVoid(VALID_PTR (pConnection));
1809 LogFlowFunc (("pConnection=%p\n", pConnection));
1810 autoDBusError dbusError;
1811
1812 dbus_bus_remove_match (pConnection,
1813 "type='signal',"
1814 "interface='org.freedesktop.Hal.Manager',"
1815 "sender='org.freedesktop.Hal',"
1816 "path='/org/freedesktop/Hal/Manager'",
1817 &dbusError.get());
1818 dbus_connection_unref (pConnection);
1819 LogFlowFunc(("returning\n"));
1820 dbusError.FlowLog();
1821}
1822
1823/**
1824 * Helper function for shutting down a private connection to DBus and hal.
1825 * @param pConnection the connection handle
1826 */
1827/* extern */
1828void VBoxHalShutdownPrivate (DBusConnection *pConnection)
1829{
1830 AssertReturnVoid(VALID_PTR (pConnection));
1831 LogFlowFunc (("pConnection=%p\n", pConnection));
1832 autoDBusError dbusError;
1833
1834 dbus_bus_remove_match (pConnection,
1835 "type='signal',"
1836 "interface='org.freedesktop.Hal.Manager',"
1837 "sender='org.freedesktop.Hal',"
1838 "path='/org/freedesktop/Hal/Manager'",
1839 &dbusError.get());
1840 dbus_connection_close (pConnection);
1841 dbus_connection_unref (pConnection);
1842 LogFlowFunc(("returning\n"));
1843 dbusError.FlowLog();
1844}
1845
1846/** Wrapper around dbus_connection_unref. We need this to use it as a real
1847 * function in auto pointers, as a function pointer won't wash here. */
1848/* extern */
1849void VBoxDBusConnectionUnref(DBusConnection *pConnection)
1850{
1851 dbus_connection_unref(pConnection);
1852}
1853
1854/**
1855 * This function closes and unrefs a private connection to dbus. It should
1856 * only be called once no-one else is referencing the connection.
1857 */
1858/* extern */
1859void VBoxDBusConnectionCloseAndUnref(DBusConnection *pConnection)
1860{
1861 dbus_connection_close(pConnection);
1862 dbus_connection_unref(pConnection);
1863}
1864
1865/** Wrapper around dbus_message_unref. We need this to use it as a real
1866 * function in auto pointers, as a function pointer won't wash here. */
1867/* extern */
1868void VBoxDBusMessageUnref(DBusMessage *pMessage)
1869{
1870 dbus_message_unref(pMessage);
1871}
1872
1873/**
1874 * Find the UDIs of hal entries that contain Key=Value property.
1875 * @returns iprt status code. If a non-fatal error occurs, we return success
1876 * but reset pMessage to NULL.
1877 * @param pConnection an initialised connection DBus
1878 * @param pszKey the property key
1879 * @param pszValue the property value
1880 * @param pMessage where to store the return DBus message. This must be
1881 * parsed to get at the UDIs. NOT optional.
1882 */
1883/* static */
1884int halFindDeviceStringMatch (DBusConnection *pConnection, const char *pszKey,
1885 const char *pszValue,
1886 RTMemAutoPtr <DBusMessage, VBoxDBusMessageUnref> *pMessage)
1887{
1888 AssertReturn( VALID_PTR (pConnection) && VALID_PTR (pszKey)
1889 && VALID_PTR (pszValue) && VALID_PTR (pMessage),
1890 VERR_INVALID_POINTER);
1891 LogFlowFunc (("pConnection=%p, pszKey=%s, pszValue=%s, pMessage=%p\n",
1892 pConnection, pszKey, pszValue, pMessage));
1893 int rc = VINF_SUCCESS; /* We set this to failure on fatal errors. */
1894 bool halSuccess = true; /* We set this to false to abort the operation. */
1895 autoDBusError dbusError;
1896
1897 RTMemAutoPtr <DBusMessage, VBoxDBusMessageUnref> message, reply;
1898 if (halSuccess && RT_SUCCESS(rc))
1899 {
1900 message = dbus_message_new_method_call ("org.freedesktop.Hal",
1901 "/org/freedesktop/Hal/Manager",
1902 "org.freedesktop.Hal.Manager",
1903 "FindDeviceStringMatch");
1904 if (!message)
1905 rc = VERR_NO_MEMORY;
1906 }
1907 if (halSuccess && RT_SUCCESS(rc))
1908 {
1909 DBusMessageIter iterAppend;
1910 dbus_message_iter_init_append (message.get(), &iterAppend);
1911 dbus_message_iter_append_basic (&iterAppend, DBUS_TYPE_STRING, &pszKey);
1912 dbus_message_iter_append_basic (&iterAppend, DBUS_TYPE_STRING, &pszValue);
1913 reply = dbus_connection_send_with_reply_and_block (pConnection,
1914 message.get(), -1,
1915 &dbusError.get());
1916 if (!reply)
1917 halSuccess = false;
1918 }
1919 *pMessage = reply.release ();
1920 LogFlowFunc (("rc=%Rrc, *pMessage.value()=%p\n", rc, (*pMessage).get()));
1921 dbusError.FlowLog();
1922 return rc;
1923}
1924
1925/**
1926 * Find the UDIs of hal entries that contain Key=Value property and return the
1927 * result on the end of a vector of iprt::MiniString.
1928 * @returns iprt status code. If a non-fatal error occurs, we return success
1929 * but set *pfSuccess to false.
1930 * @param pConnection an initialised connection DBus
1931 * @param pszKey the property key
1932 * @param pszValue the property value
1933 * @param pMatches pointer to an array of iprt::MiniString to append the
1934 * results to. NOT optional.
1935 * @param pfSuccess will be set to true if the operation succeeds
1936 */
1937/* static */
1938int halFindDeviceStringMatchVector (DBusConnection *pConnection,
1939 const char *pszKey, const char *pszValue,
1940 std::vector<iprt::MiniString> *pMatches,
1941 bool *pfSuccess)
1942{
1943 AssertPtrReturn (pConnection, VERR_INVALID_POINTER);
1944 AssertPtrReturn (pszKey, VERR_INVALID_POINTER);
1945 AssertPtrReturn (pszValue, VERR_INVALID_POINTER);
1946 AssertPtrReturn (pMatches, VERR_INVALID_POINTER);
1947 AssertReturn(pfSuccess == NULL || VALID_PTR (pfSuccess), VERR_INVALID_POINTER);
1948 LogFlowFunc (("pConnection=%p, pszKey=%s, pszValue=%s, pMatches=%p, pfSuccess=%p\n",
1949 pConnection, pszKey, pszValue, pMatches, pfSuccess));
1950 int rc = VINF_SUCCESS; /* We set this to failure on fatal errors. */
1951 bool halSuccess = true; /* We set this to false to abort the operation. */
1952
1953 RTMemAutoPtr <DBusMessage, VBoxDBusMessageUnref> message, replyFind;
1954 DBusMessageIter iterFind, iterUdis;
1955
1956 if (halSuccess && RT_SUCCESS(rc))
1957 {
1958 rc = halFindDeviceStringMatch (pConnection, pszKey, pszValue,
1959 &replyFind);
1960 if (!replyFind)
1961 halSuccess = false;
1962 }
1963 if (halSuccess && RT_SUCCESS(rc))
1964 {
1965 dbus_message_iter_init (replyFind.get(), &iterFind);
1966 if (dbus_message_iter_get_arg_type (&iterFind) != DBUS_TYPE_ARRAY)
1967 halSuccess = false;
1968 }
1969 if (halSuccess && RT_SUCCESS(rc))
1970 dbus_message_iter_recurse (&iterFind, &iterUdis);
1971 for (; halSuccess && RT_SUCCESS(rc)
1972 && dbus_message_iter_get_arg_type (&iterUdis) == DBUS_TYPE_STRING;
1973 dbus_message_iter_next(&iterUdis))
1974 {
1975 /* Now get all UDIs from the iterator */
1976 const char *pszUdi;
1977 dbus_message_iter_get_basic (&iterUdis, &pszUdi);
1978 try
1979 {
1980 pMatches->push_back(pszUdi);
1981 }
1982 catch(std::bad_alloc &e)
1983 {
1984 rc = VERR_NO_MEMORY;
1985 }
1986 }
1987 if (pfSuccess != NULL)
1988 *pfSuccess = halSuccess;
1989 LogFlow (("rc=%Rrc, halSuccess=%d\n", rc, halSuccess));
1990 return rc;
1991}
1992
1993/**
1994 * Read a set of string properties for a device. If some of the properties are
1995 * not of type DBUS_TYPE_STRING or do not exist then a NULL pointer will be
1996 * returned for them.
1997 * @returns iprt status code. If the operation failed for non-fatal reasons
1998 * then we return success and leave pMessage untouched - reset it
1999 * before the call to detect this.
2000 * @param pConnection an initialised connection DBus
2001 * @param pszUdi the Udi of the device
2002 * @param cProps the number of property values to look up
2003 * @param papszKeys the keys of the properties to be looked up
2004 * @param papszValues where to store the values of the properties. The
2005 * strings returned will be valid until the message
2006 * returned in @a ppMessage is freed. Undefined if
2007 * the message is NULL.
2008 * @param pMessage where to store the return DBus message. The caller
2009 * is responsible for freeing this once they have
2010 * finished with the value strings. NOT optional.
2011 */
2012/* static */
2013int halGetPropertyStrings (DBusConnection *pConnection, const char *pszUdi,
2014 size_t cProps, const char **papszKeys,
2015 char **papszValues,
2016 RTMemAutoPtr <DBusMessage, VBoxDBusMessageUnref> *pMessage)
2017{
2018 AssertReturn( VALID_PTR (pConnection) && VALID_PTR (pszUdi)
2019 && VALID_PTR (papszKeys) && VALID_PTR (papszValues)
2020 && VALID_PTR (pMessage),
2021 VERR_INVALID_POINTER);
2022 LogFlowFunc (("pConnection=%p, pszUdi=%s, cProps=%llu, papszKeys=%p, papszValues=%p, pMessage=%p\n",
2023 pConnection, pszUdi, cProps, papszKeys, papszValues, pMessage));
2024 int rc = VINF_SUCCESS; /* We set this to failure on fatal errors. */
2025 bool halSuccess = true; /* We set this to false to abort the operation. */
2026 autoDBusError dbusError;
2027
2028 RTMemAutoPtr <DBusMessage, VBoxDBusMessageUnref> message, reply;
2029 DBusMessageIter iterGet, iterProps;
2030
2031 /* Initialise the return array to NULLs */
2032 for (size_t i = 0; i < cProps; ++i)
2033 papszValues[i] = NULL;
2034
2035 /* Send a GetAllProperties message to hald */
2036 message = dbus_message_new_method_call ("org.freedesktop.Hal", pszUdi,
2037 "org.freedesktop.Hal.Device",
2038 "GetAllProperties");
2039 if (!message)
2040 rc = VERR_NO_MEMORY;
2041 if (halSuccess && RT_SUCCESS(rc))
2042 {
2043 reply = dbus_connection_send_with_reply_and_block (pConnection,
2044 message.get(), -1,
2045 &dbusError.get());
2046 if (!reply)
2047 halSuccess = false;
2048 }
2049
2050 /* Parse the reply */
2051 if (halSuccess && RT_SUCCESS(rc))
2052 {
2053 dbus_message_iter_init (reply.get(), &iterGet);
2054 if ( dbus_message_iter_get_arg_type (&iterGet) != DBUS_TYPE_ARRAY
2055 && dbus_message_iter_get_element_type (&iterGet) != DBUS_TYPE_DICT_ENTRY)
2056 halSuccess = false;
2057 }
2058 if (halSuccess && RT_SUCCESS(rc))
2059 dbus_message_iter_recurse (&iterGet, &iterProps);
2060 /* Go through all entries in the reply and see if any match our keys. */
2061 while ( halSuccess && RT_SUCCESS(rc)
2062 && dbus_message_iter_get_arg_type (&iterProps)
2063 == DBUS_TYPE_DICT_ENTRY)
2064 {
2065 const char *pszKey;
2066 DBusMessageIter iterEntry, iterValue;
2067 dbus_message_iter_recurse (&iterProps, &iterEntry);
2068 dbus_message_iter_get_basic (&iterEntry, &pszKey);
2069 dbus_message_iter_next (&iterEntry);
2070 dbus_message_iter_recurse (&iterEntry, &iterValue);
2071 /* Fill in any matches. */
2072 for (size_t i = 0; i < cProps; ++i)
2073 if (strcmp (pszKey, papszKeys[i]) == 0)
2074 {
2075 if (dbus_message_iter_get_arg_type (&iterValue) == DBUS_TYPE_STRING)
2076 dbus_message_iter_get_basic (&iterValue, &papszValues[i]);
2077 }
2078 dbus_message_iter_next (&iterProps);
2079 }
2080 if (RT_SUCCESS(rc) && halSuccess)
2081 *pMessage = reply.release();
2082 if (dbusError.HasName (DBUS_ERROR_NO_MEMORY))
2083 rc = VERR_NO_MEMORY;
2084 LogFlowFunc (("rc=%Rrc, *pMessage.value()=%p\n", rc, (*pMessage).get()));
2085 dbusError.FlowLog();
2086 return rc;
2087}
2088
2089/**
2090 * Read a set of string properties for a device. If some properties do not
2091 * exist or are not of type DBUS_TYPE_STRING, we will still fetch the others.
2092 * @returns iprt status code. If the operation failed for non-fatal reasons
2093 * then we return success and set *pfSuccess to false.
2094 * @param pConnection an initialised connection DBus
2095 * @param pszUdi the Udi of the device
2096 * @param cProps the number of property values to look up
2097 * @param papszKeys the keys of the properties to be looked up
2098 * @param pMatches pointer to an empty array of iprt::MiniString to append the
2099 * results to. NOT optional.
2100 * @param pfMatches pointer to an array of boolean values indicating
2101 * whether the respective property is a string. If this
2102 * is not supplied then all properties must be strings
2103 * for the operation to be considered successful
2104 * @param pfSuccess will be set to true if the operation succeeds
2105 */
2106/* static */
2107int halGetPropertyStringsVector (DBusConnection *pConnection,
2108 const char *pszUdi, size_t cProps,
2109 const char **papszKeys,
2110 std::vector<iprt::MiniString> *pMatches,
2111 bool *pfMatches, bool *pfSuccess)
2112{
2113 AssertPtrReturn (pConnection, VERR_INVALID_POINTER);
2114 AssertPtrReturn (pszUdi, VERR_INVALID_POINTER);
2115 AssertPtrReturn (papszKeys, VERR_INVALID_POINTER);
2116 AssertPtrReturn (pMatches, VERR_INVALID_POINTER);
2117 AssertReturn((pfMatches == NULL) || VALID_PTR (pfMatches), VERR_INVALID_POINTER);
2118 AssertReturn((pfSuccess == NULL) || VALID_PTR (pfSuccess), VERR_INVALID_POINTER);
2119 AssertReturn(pMatches->empty(), VERR_INVALID_PARAMETER);
2120 LogFlowFunc (("pConnection=%p, pszUdi=%s, cProps=%llu, papszKeys=%p, pMatches=%p, pfMatches=%p, pfSuccess=%p\n",
2121 pConnection, pszUdi, cProps, papszKeys, pMatches, pfMatches, pfSuccess));
2122 RTMemAutoPtr <char *> values(cProps);
2123 RTMemAutoPtr <DBusMessage, VBoxDBusMessageUnref> message;
2124 bool halSuccess = true;
2125 int rc = halGetPropertyStrings (pConnection, pszUdi, cProps, papszKeys,
2126 values.get(), &message);
2127 if (!message)
2128 halSuccess = false;
2129 for (size_t i = 0; RT_SUCCESS(rc) && halSuccess && i < cProps; ++i)
2130 {
2131 bool fMatches = values[i] != NULL;
2132 if (pfMatches != NULL)
2133 pfMatches[i] = fMatches;
2134 else
2135 halSuccess = fMatches;
2136 try
2137 {
2138 pMatches->push_back(fMatches ? values[i] : "");
2139 }
2140 catch(std::bad_alloc &e)
2141 {
2142 rc = VERR_NO_MEMORY;
2143 }
2144 }
2145 if (pfSuccess != NULL)
2146 *pfSuccess = halSuccess;
2147 if (RT_SUCCESS(rc) && halSuccess)
2148 {
2149 Assert(pMatches->size() == cProps);
2150 AssertForEach(j, size_t, 0, cProps, (pfMatches == NULL)
2151 || (pfMatches[j] == true)
2152 || ((pfMatches[j] == false) && (pMatches[j].size() == 0)));
2153 }
2154 LogFlowFunc (("rc=%Rrc, halSuccess=%d\n", rc, halSuccess));
2155 return rc;
2156}
2157
2158
2159/**
2160 * Helper function to query the hal subsystem for information about USB devices
2161 * attached to the system.
2162 * @returns iprt status code
2163 * @param pList where to add information about the devices detected
2164 * @param pfSuccess will be set to true if all interactions with hal
2165 * succeeded and to false otherwise. Optional.
2166 *
2167 * @returns IPRT status code
2168 */
2169/* static */
2170int getUSBDeviceInfoFromHal(USBDeviceInfoList *pList, bool *pfSuccess)
2171{
2172 AssertReturn(VALID_PTR (pList) && (pfSuccess == NULL || VALID_PTR (pfSuccess)),
2173 VERR_INVALID_POINTER);
2174 LogFlowFunc (("pList=%p, pfSuccess=%p\n", pList, pfSuccess));
2175 int rc = VINF_SUCCESS; /* We set this to failure on fatal errors. */
2176 bool halSuccess = true; /* We set this to false to abort the operation. */
2177 autoDBusError dbusError;
2178
2179 RTMemAutoPtr<DBusMessage, VBoxDBusMessageUnref> message, replyFind, replyGet;
2180 RTMemAutoPtr<DBusConnection, VBoxHalShutdown> dbusConnection;
2181 DBusMessageIter iterFind, iterUdis;
2182
2183 /* Connect to hal */
2184 rc = halInit (&dbusConnection);
2185 if (!dbusConnection)
2186 halSuccess = false;
2187 /* Get an array of all devices in the usb_device subsystem */
2188 if (halSuccess && RT_SUCCESS(rc))
2189 {
2190 rc = halFindDeviceStringMatch(dbusConnection.get(), "info.subsystem",
2191 "usb_device", &replyFind);
2192 if (!replyFind)
2193 halSuccess = false;
2194 }
2195 if (halSuccess && RT_SUCCESS(rc))
2196 {
2197 dbus_message_iter_init(replyFind.get(), &iterFind);
2198 if (dbus_message_iter_get_arg_type (&iterFind) != DBUS_TYPE_ARRAY)
2199 halSuccess = false;
2200 }
2201 /* Recurse down into the array and query interesting information about the
2202 * entries. */
2203 if (halSuccess && RT_SUCCESS(rc))
2204 dbus_message_iter_recurse(&iterFind, &iterUdis);
2205 for (; halSuccess && RT_SUCCESS(rc)
2206 && dbus_message_iter_get_arg_type(&iterUdis) == DBUS_TYPE_STRING;
2207 dbus_message_iter_next(&iterUdis))
2208 {
2209 /* Get the device node and the sysfs path for the current entry. */
2210 const char *pszUdi;
2211 dbus_message_iter_get_basic (&iterUdis, &pszUdi);
2212 static const char *papszKeys[] = { "linux.device_file", "linux.sysfs_path" };
2213 char *papszValues[RT_ELEMENTS(papszKeys)];
2214 rc = halGetPropertyStrings(dbusConnection.get(), pszUdi, RT_ELEMENTS(papszKeys),
2215 papszKeys, papszValues, &replyGet);
2216 const char *pszDevice = papszValues[0], *pszSysfsPath = papszValues[1];
2217 /* Get the interfaces. */
2218 if (!!replyGet && pszDevice && pszSysfsPath)
2219 {
2220 USBDeviceInfo info(pszDevice, pszSysfsPath);
2221 bool ifaceSuccess = true; /* If we can't get the interfaces, just
2222 * skip this one device. */
2223 rc = getUSBInterfacesFromHal(&info.mInterfaces, pszUdi, &ifaceSuccess);
2224 if (RT_SUCCESS(rc) && halSuccess && ifaceSuccess)
2225 try
2226 {
2227 pList->push_back(info);
2228 }
2229 catch(std::bad_alloc &e)
2230 {
2231 rc = VERR_NO_MEMORY;
2232 }
2233 }
2234 }
2235 if (dbusError.HasName (DBUS_ERROR_NO_MEMORY))
2236 rc = VERR_NO_MEMORY;
2237 if (pfSuccess != NULL)
2238 *pfSuccess = halSuccess;
2239 LogFlow(("rc=%Rrc, halSuccess=%d\n", rc, halSuccess));
2240 dbusError.FlowLog();
2241 return rc;
2242}
2243
2244/**
2245 * Helper function to query the hal subsystem for information about USB devices
2246 * attached to the system, using the older API.
2247 * @returns iprt status code
2248 * @param pList where to add information about the devices detected
2249 * @param pfSuccess will be set to true if all interactions with hal
2250 * succeeded and to false otherwise. Optional.
2251 *
2252 * @returns IPRT status code
2253 */
2254/* static */
2255int getOldUSBDeviceInfoFromHal(USBDeviceInfoList *pList, bool *pfSuccess)
2256{
2257 AssertReturn(VALID_PTR (pList) && (pfSuccess == NULL || VALID_PTR (pfSuccess)),
2258 VERR_INVALID_POINTER);
2259 LogFlowFunc (("pList=%p, pfSuccess=%p\n", pList, pfSuccess));
2260 int rc = VINF_SUCCESS; /* We set this to failure on fatal errors. */
2261 bool halSuccess = true; /* We set this to false to abort the operation. */
2262 autoDBusError dbusError;
2263
2264 RTMemAutoPtr<DBusMessage, VBoxDBusMessageUnref> message, replyFind, replyGet;
2265 RTMemAutoPtr<DBusConnection, VBoxHalShutdown> dbusConnection;
2266 DBusMessageIter iterFind, iterUdis;
2267
2268 /* Connect to hal */
2269 rc = halInit(&dbusConnection);
2270 if (!dbusConnection)
2271 halSuccess = false;
2272 /* Get an array of all devices in the usb_device subsystem */
2273 if (halSuccess && RT_SUCCESS(rc))
2274 {
2275 rc = halFindDeviceStringMatch(dbusConnection.get(), "info.category",
2276 "usbraw", &replyFind);
2277 if (!replyFind)
2278 halSuccess = false;
2279 }
2280 if (halSuccess && RT_SUCCESS(rc))
2281 {
2282 dbus_message_iter_init(replyFind.get(), &iterFind);
2283 if (dbus_message_iter_get_arg_type(&iterFind) != DBUS_TYPE_ARRAY)
2284 halSuccess = false;
2285 }
2286 /* Recurse down into the array and query interesting information about the
2287 * entries. */
2288 if (halSuccess && RT_SUCCESS(rc))
2289 dbus_message_iter_recurse(&iterFind, &iterUdis);
2290 for (; halSuccess && RT_SUCCESS(rc)
2291 && dbus_message_iter_get_arg_type(&iterUdis) == DBUS_TYPE_STRING;
2292 dbus_message_iter_next(&iterUdis))
2293 {
2294 /* Get the device node and the sysfs path for the current entry. */
2295 const char *pszUdi;
2296 dbus_message_iter_get_basic(&iterUdis, &pszUdi);
2297 static const char *papszKeys[] = { "linux.device_file", "info.parent" };
2298 char *papszValues[RT_ELEMENTS(papszKeys)];
2299 rc = halGetPropertyStrings(dbusConnection.get(), pszUdi, RT_ELEMENTS(papszKeys),
2300 papszKeys, papszValues, &replyGet);
2301 const char *pszDevice = papszValues[0], *pszSysfsPath = papszValues[1];
2302 /* Get the interfaces. */
2303 if (!!replyGet && pszDevice && pszSysfsPath)
2304 {
2305 USBDeviceInfo info(pszDevice, pszSysfsPath);
2306 bool ifaceSuccess = false; /* If we can't get the interfaces, just
2307 * skip this one device. */
2308 rc = getUSBInterfacesFromHal(&info.mInterfaces, pszSysfsPath,
2309 &ifaceSuccess);
2310 if (RT_SUCCESS(rc) && halSuccess && ifaceSuccess)
2311 try
2312 {
2313 pList->push_back(info);
2314 }
2315 catch(std::bad_alloc &e)
2316 {
2317 rc = VERR_NO_MEMORY;
2318 }
2319 }
2320 }
2321 if (dbusError.HasName(DBUS_ERROR_NO_MEMORY))
2322 rc = VERR_NO_MEMORY;
2323 if (pfSuccess != NULL)
2324 *pfSuccess = halSuccess;
2325 LogFlow(("rc=%Rrc, halSuccess=%d\n", rc, halSuccess));
2326 dbusError.FlowLog();
2327 return rc;
2328}
2329
2330
2331/**
2332 * Helper function to query the hal subsystem for information about USB devices
2333 * attached to the system.
2334 * @returns iprt status code
2335 * @param pList where to add information about the devices detected. If
2336 * certain interfaces are not found (@a pfFound is false on
2337 * return) this may contain invalid information.
2338 * @param pcszUdi the hal UDI of the device
2339 * @param pfSuccess will be set to true if the operation succeeds and to
2340 * false if it fails for non-critical reasons. Optional.
2341 *
2342 * @returns IPRT status code
2343 */
2344/* static */
2345int getUSBInterfacesFromHal(std::vector<iprt::MiniString> *pList,
2346 const char *pcszUdi, bool *pfSuccess)
2347{
2348 AssertReturn(VALID_PTR(pList) && VALID_PTR(pcszUdi) &&
2349 (pfSuccess == NULL || VALID_PTR (pfSuccess)),
2350 VERR_INVALID_POINTER);
2351 LogFlowFunc(("pList=%p, pcszUdi=%s, pfSuccess=%p\n", pList, pcszUdi,
2352 pfSuccess));
2353 int rc = VINF_SUCCESS; /* We set this to failure on fatal errors. */
2354 bool halSuccess = true; /* We set this to false to abort the operation. */
2355 autoDBusError dbusError;
2356
2357 RTMemAutoPtr <DBusMessage, VBoxDBusMessageUnref> message, replyFind, replyGet;
2358 RTMemAutoPtr <DBusConnection, VBoxHalShutdown> dbusConnection;
2359 DBusMessageIter iterFind, iterUdis;
2360
2361 rc = halInit(&dbusConnection);
2362 if (!dbusConnection)
2363 halSuccess = false;
2364 if (halSuccess && RT_SUCCESS(rc))
2365 {
2366 /* Look for children of the current UDI. */
2367 rc = halFindDeviceStringMatch(dbusConnection.get(), "info.parent",
2368 pcszUdi, &replyFind);
2369 if (!replyFind)
2370 halSuccess = false;
2371 }
2372 if (halSuccess && RT_SUCCESS(rc))
2373 {
2374 dbus_message_iter_init(replyFind.get(), &iterFind);
2375 if (dbus_message_iter_get_arg_type(&iterFind) != DBUS_TYPE_ARRAY)
2376 halSuccess = false;
2377 }
2378 if (halSuccess && RT_SUCCESS(rc))
2379 dbus_message_iter_recurse(&iterFind, &iterUdis);
2380 for (; halSuccess && RT_SUCCESS(rc)
2381 && dbus_message_iter_get_arg_type(&iterUdis) == DBUS_TYPE_STRING;
2382 dbus_message_iter_next(&iterUdis))
2383 {
2384 /* Now get the sysfs path and the subsystem from the iterator */
2385 const char *pszUdi;
2386 dbus_message_iter_get_basic(&iterUdis, &pszUdi);
2387 static const char *papszKeys[] = { "linux.sysfs_path", "info.subsystem",
2388 "linux.subsystem" };
2389 char *papszValues[RT_ELEMENTS(papszKeys)];
2390 rc = halGetPropertyStrings(dbusConnection.get(), pszUdi, RT_ELEMENTS(papszKeys),
2391 papszKeys, papszValues, &replyGet);
2392 const char *pszSysfsPath = papszValues[0], *pszInfoSubsystem = papszValues[1],
2393 *pszLinuxSubsystem = papszValues[2];
2394 if (!replyGet)
2395 halSuccess = false;
2396 if (!!replyGet && pszSysfsPath == NULL)
2397 halSuccess = false;
2398 if ( halSuccess && RT_SUCCESS(rc)
2399 && RTStrCmp (pszInfoSubsystem, "usb_device") != 0 /* Children of buses can also be devices. */
2400 && RTStrCmp (pszLinuxSubsystem, "usb_device") != 0)
2401 try
2402 {
2403 pList->push_back(pszSysfsPath);
2404 }
2405 catch(std::bad_alloc &e)
2406 {
2407 rc = VERR_NO_MEMORY;
2408 }
2409 }
2410 if (dbusError.HasName(DBUS_ERROR_NO_MEMORY))
2411 rc = VERR_NO_MEMORY;
2412 if (pfSuccess != NULL)
2413 *pfSuccess = halSuccess;
2414 LogFlow(("rc=%Rrc, halSuccess=%d\n", rc, halSuccess));
2415 dbusError.FlowLog();
2416 return rc;
2417}
2418
2419/**
2420 * When it is registered with DBus, this function will be called by
2421 * dbus_connection_read_write_dispatch each time a message is received over the
2422 * DBus connection. We check whether that message was caused by a hal device
2423 * hotplug event, and if so we set a flag. dbus_connection_read_write_dispatch
2424 * will return after calling its filter functions, and its caller should then
2425 * check the status of the flag passed to the filter function.
2426 *
2427 * @param pConnection The DBus connection we are using.
2428 * @param pMessage The DBus message which just arrived.
2429 * @param pvUser A pointer to the flag variable we are to set.
2430 */
2431/* static */
2432DBusHandlerResult dbusFilterFunction(DBusConnection * /* pConnection */,
2433 DBusMessage *pMessage, void *pvUser)
2434{
2435 volatile bool *pTriggered = reinterpret_cast<volatile bool *>(pvUser);
2436 if ( dbus_message_is_signal(pMessage, "org.freedesktop.Hal.Manager",
2437 "DeviceAdded")
2438 || dbus_message_is_signal(pMessage, "org.freedesktop.Hal.Manager",
2439 "DeviceRemoved"))
2440 {
2441 *pTriggered = true;
2442 }
2443 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
2444}
2445#endif /* RT_OS_LINUX && VBOX_WITH_DBUS */
2446
Note: See TracBrowser for help on using the repository browser.

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