VirtualBox

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

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

more warnings.

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