VirtualBox

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

Last change on this file since 23522 was 23522, checked in by vboxsync, 16 years ago

Main/HostHardwareLinux: more rewriting of the Linux host drive code, including a complete rewrite of the legacy code

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