VirtualBox

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

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

Main/linux/USB: don't call close(-1)

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