VirtualBox

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

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

Main/HostHardwareLinux: fix an assertion followed by a NULL-pointer parameter segfault in the new USB code. Not relevant for 3.1 due to code differences

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