VirtualBox

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

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

Main/HostHardwareLinux: reconnect to the FAM daemon if the connection breaks

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

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