VirtualBox

source: vbox/trunk/src/VBox/Main/src-server/linux/HostHardwareLinux.cpp@ 85932

Last change on this file since 85932 was 85932, checked in by vboxsync, 5 years ago

Main/HostHardwareLinux.cpp: doxygen fix. bugref:9224

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 42.7 KB
Line 
1/* $Id: HostHardwareLinux.cpp 85932 2020-08-28 16:46:50Z vboxsync $ */
2/** @file
3 * VirtualBox Main - Code for handling hardware detection under Linux, VBoxSVC.
4 */
5
6/*
7 * Copyright (C) 2008-2020 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.215389.xyz. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#define LOG_GROUP LOG_GROUP_MAIN
23#include "HostHardwareLinux.h"
24
25#include <VBox/err.h>
26#include <VBox/log.h>
27
28#include <iprt/asm.h>
29#include <iprt/dir.h>
30#include <iprt/env.h>
31#include <iprt/file.h>
32#include <iprt/mem.h>
33#include <iprt/param.h>
34#include <iprt/path.h>
35#include <iprt/string.h>
36
37#include <linux/cdrom.h>
38#include <linux/fd.h>
39#include <linux/major.h>
40#include <scsi/scsi.h>
41
42#include <iprt/linux/sysfs.h>
43
44#ifdef VBOX_USB_WITH_SYSFS
45# ifdef VBOX_USB_WITH_INOTIFY
46# include <dlfcn.h>
47# include <fcntl.h>
48# include <poll.h>
49# include <signal.h>
50# include <unistd.h>
51# endif
52#endif
53
54#include <vector>
55
56#include <errno.h>
57#include <dirent.h>
58#include <limits.h>
59#include <stdio.h>
60#include <stdlib.h>
61#include <sys/types.h>
62#include <sys/sysmacros.h>
63
64
65/*********************************************************************************************************************************
66* Global Variables *
67*********************************************************************************************************************************/
68#ifdef TESTCASE
69static bool testing() { return true; }
70static bool fNoProbe = false;
71static bool noProbe() { return fNoProbe; }
72static void setNoProbe(bool val) { fNoProbe = val; }
73#else
74static bool testing() { return false; }
75static bool noProbe() { return false; }
76static void setNoProbe(bool val) { (void)val; }
77#endif
78
79
80/*********************************************************************************************************************************
81* Typedefs and Defines *
82*********************************************************************************************************************************/
83typedef enum SysfsWantDevice_T
84{
85 DVD,
86 Floppy,
87 FixedDisk
88} SysfsWantDevice_T;
89
90
91/*********************************************************************************************************************************
92* Internal Functions *
93*********************************************************************************************************************************/
94static int getDriveInfoFromEnv(const char *pcszVar, DriveInfoList *pList, bool isDVD, bool *pfSuccess) RT_NOTHROW_PROTO;
95static int getDriveInfoFromSysfs(DriveInfoList *pList, SysfsWantDevice_T wantDevice, bool *pfSuccess) RT_NOTHROW_PROTO;
96
97
98/** Find the length of a string, ignoring trailing non-ascii or control
99 * characters
100 * @note Code duplicated in HostHardwareFreeBSD.cpp */
101static size_t strLenStripped(const char *pcsz) RT_NOTHROW_DEF
102{
103 size_t cch = 0;
104 for (size_t i = 0; pcsz[i] != '\0'; ++i)
105 if (pcsz[i] > 32 /*space*/ && pcsz[i] < 127 /*delete*/)
106 cch = i;
107 return cch + 1;
108}
109
110
111/**
112 * Get the name of a floppy drive according to the Linux floppy driver.
113 * @returns true on success, false if the name was not available (i.e. the
114 * device was not readable, or the file name wasn't a PC floppy
115 * device)
116 * @param pcszNode the path to the device node for the device
117 * @param Number the Linux floppy driver number for the drive. Required.
118 * @param pszName where to store the name retrieved
119 */
120static bool floppyGetName(const char *pcszNode, unsigned Number, floppy_drive_name pszName) RT_NOTHROW_DEF
121{
122 AssertPtrReturn(pcszNode, false);
123 AssertPtrReturn(pszName, false);
124 AssertReturn(Number <= 7, false);
125 RTFILE File;
126 int rc = RTFileOpen(&File, pcszNode, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE | RTFILE_O_NON_BLOCK);
127 if (RT_SUCCESS(rc))
128 {
129 int rcIoCtl;
130 rc = RTFileIoCtl(File, FDGETDRVTYP, pszName, 0, &rcIoCtl);
131 RTFileClose(File);
132 if (RT_SUCCESS(rc) && rcIoCtl >= 0)
133 return true;
134 }
135 return false;
136}
137
138
139/**
140 * Create a UDI and a description for a floppy drive based on a number and the
141 * driver's name for it. We deliberately return an ugly sequence of
142 * characters as the description rather than an English language string to
143 * avoid translation issues.
144 *
145 * @returns true if we know the device to be valid, false otherwise
146 * @param pcszName the floppy driver name for the device (optional)
147 * @param Number the number of the floppy (0 to 3 on FDC 0, 4 to 7 on
148 * FDC 1)
149 * @param pszDesc where to store the device description (optional)
150 * @param cbDesc the size of the buffer in @a pszDesc
151 * @param pszUdi where to store the device UDI (optional)
152 * @param cbUdi the size of the buffer in @a pszUdi
153 */
154static void floppyCreateDeviceStrings(const floppy_drive_name pcszName, unsigned Number,
155 char *pszDesc, size_t cbDesc, char *pszUdi, size_t cbUdi) RT_NOTHROW_DEF
156{
157 AssertPtrNullReturnVoid(pcszName);
158 AssertPtrNullReturnVoid(pszDesc);
159 AssertReturnVoid(!pszDesc || cbDesc > 0);
160 AssertPtrNullReturnVoid(pszUdi);
161 AssertReturnVoid(!pszUdi || cbUdi > 0);
162 AssertReturnVoid(Number <= 7);
163 if (pcszName)
164 {
165 const char *pcszSize;
166 switch(pcszName[0])
167 {
168 case 'd': case 'q': case 'h':
169 pcszSize = "5.25\"";
170 break;
171 case 'D': case 'H': case 'E': case 'u':
172 pcszSize = "3.5\"";
173 break;
174 default:
175 pcszSize = "(unknown)";
176 }
177 if (pszDesc)
178 RTStrPrintf(pszDesc, cbDesc, "%s %s K%s", pcszSize, &pcszName[1],
179 Number > 3 ? ", FDC 2" : "");
180 }
181 else
182 {
183 if (pszDesc)
184 RTStrPrintf(pszDesc, cbDesc, "FDD %d%s", (Number & 4) + 1,
185 Number > 3 ? ", FDC 2" : "");
186 }
187 if (pszUdi)
188 RTStrPrintf(pszUdi, cbUdi,
189 "/org/freedesktop/Hal/devices/platform_floppy_%u_storage",
190 Number);
191}
192
193
194/**
195 * Check whether a device number might correspond to a CD-ROM device according
196 * to Documentation/devices.txt in the Linux kernel source.
197 *
198 * @returns true if it might, false otherwise
199 * @param Number the device number (major and minor combination)
200 */
201static bool isCdromDevNum(dev_t Number) RT_NOTHROW_DEF
202{
203 int major = major(Number);
204 int minor = minor(Number);
205 if (major == IDE0_MAJOR && !(minor & 0x3f))
206 return true;
207 if (major == SCSI_CDROM_MAJOR)
208 return true;
209 if (major == CDU31A_CDROM_MAJOR)
210 return true;
211 if (major == GOLDSTAR_CDROM_MAJOR)
212 return true;
213 if (major == OPTICS_CDROM_MAJOR)
214 return true;
215 if (major == SANYO_CDROM_MAJOR)
216 return true;
217 if (major == MITSUMI_X_CDROM_MAJOR)
218 return true;
219 if (major == IDE1_MAJOR && !(minor & 0x3f))
220 return true;
221 if (major == MITSUMI_CDROM_MAJOR)
222 return true;
223 if (major == CDU535_CDROM_MAJOR)
224 return true;
225 if (major == MATSUSHITA_CDROM_MAJOR)
226 return true;
227 if (major == MATSUSHITA_CDROM2_MAJOR)
228 return true;
229 if (major == MATSUSHITA_CDROM3_MAJOR)
230 return true;
231 if (major == MATSUSHITA_CDROM4_MAJOR)
232 return true;
233 if (major == AZTECH_CDROM_MAJOR)
234 return true;
235 if (major == 30 /* CM205_CDROM_MAJOR */) /* no #define for some reason */
236 return true;
237 if (major == CM206_CDROM_MAJOR)
238 return true;
239 if (major == IDE3_MAJOR && !(minor & 0x3f))
240 return true;
241 if (major == 46 /* Parallel port ATAPI CD-ROM */) /* no #define */
242 return true;
243 if (major == IDE4_MAJOR && !(minor & 0x3f))
244 return true;
245 if (major == IDE5_MAJOR && !(minor & 0x3f))
246 return true;
247 if (major == IDE6_MAJOR && !(minor & 0x3f))
248 return true;
249 if (major == IDE7_MAJOR && !(minor & 0x3f))
250 return true;
251 if (major == IDE8_MAJOR && !(minor & 0x3f))
252 return true;
253 if (major == IDE9_MAJOR && !(minor & 0x3f))
254 return true;
255 if (major == 113 /* VIOCD_MAJOR */)
256 return true;
257 return false;
258}
259
260
261/**
262 * Send an SCSI INQUIRY command to a device and return selected information.
263 *
264 * @returns iprt status code
265 * @retval VERR_TRY_AGAIN if the query failed but might succeed next time
266 * @param pcszNode the full path to the device node
267 * @param pbType where to store the SCSI device type on success (optional)
268 * @param pszVendor where to store the vendor id string on success (optional)
269 * @param cbVendor the size of the @a pszVendor buffer
270 * @param pszModel where to store the product id string on success (optional)
271 * @param cbModel the size of the @a pszModel buffer
272 * @note check documentation on the SCSI INQUIRY command and the Linux kernel
273 * SCSI headers included above if you want to understand what is going
274 * on in this method.
275 */
276static int cdromDoInquiry(const char *pcszNode, uint8_t *pbType, char *pszVendor, size_t cbVendor,
277 char *pszModel, size_t cbModel) RT_NOTHROW_DEF
278{
279 LogRelFlowFunc(("pcszNode=%s, pbType=%p, pszVendor=%p, cbVendor=%zu, pszModel=%p, cbModel=%zu\n",
280 pcszNode, pbType, pszVendor, cbVendor, pszModel, cbModel));
281 AssertPtrReturn(pcszNode, VERR_INVALID_POINTER);
282 AssertPtrNullReturn(pbType, VERR_INVALID_POINTER);
283 AssertPtrNullReturn(pszVendor, VERR_INVALID_POINTER);
284 AssertPtrNullReturn(pszModel, VERR_INVALID_POINTER);
285
286 RTFILE hFile = NIL_RTFILE;
287 int rc = RTFileOpen(&hFile, pcszNode, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE | RTFILE_O_NON_BLOCK);
288 if (RT_SUCCESS(rc))
289 {
290 int rcIoCtl = 0;
291 unsigned char auchResponse[96] = { 0 };
292 struct cdrom_generic_command CdromCommandReq;
293 RT_ZERO(CdromCommandReq);
294 CdromCommandReq.cmd[0] = INQUIRY;
295 CdromCommandReq.cmd[4] = sizeof(auchResponse);
296 CdromCommandReq.buffer = auchResponse;
297 CdromCommandReq.buflen = sizeof(auchResponse);
298 CdromCommandReq.data_direction = CGC_DATA_READ;
299 CdromCommandReq.timeout = 5000; /* ms */
300 rc = RTFileIoCtl(hFile, CDROM_SEND_PACKET, &CdromCommandReq, 0, &rcIoCtl);
301 if (RT_SUCCESS(rc) && rcIoCtl < 0)
302 rc = RTErrConvertFromErrno(-CdromCommandReq.stat);
303 RTFileClose(hFile);
304
305 if (RT_SUCCESS(rc))
306 {
307 if (pbType)
308 *pbType = auchResponse[0] & 0x1f;
309 if (pszVendor)
310 {
311 RTStrPrintf(pszVendor, cbVendor, "%.8s", &auchResponse[8] /* vendor id string */);
312 RTStrPurgeEncoding(pszVendor);
313 }
314 if (pszModel)
315 {
316 RTStrPrintf(pszModel, cbModel, "%.16s", &auchResponse[16] /* product id string */);
317 RTStrPurgeEncoding(pszModel);
318 }
319 LogRelFlowFunc(("returning success: type=%u, vendor=%.8s, product=%.16s\n",
320 auchResponse[0] & 0x1f, &auchResponse[8], &auchResponse[16]));
321 return VINF_SUCCESS;
322 }
323 }
324 LogRelFlowFunc(("returning %Rrc\n", rc));
325 return rc;
326}
327
328
329/**
330 * Initialise the device strings (description and UDI) for a DVD drive based on
331 * vendor and model name strings.
332 * @param pcszVendor the vendor ID string
333 * @param pcszModel the product ID string
334 * @param pszDesc where to store the description string (optional)
335 * @param cbDesc the size of the buffer in @a pszDesc
336 * @param pszUdi where to store the UDI string (optional)
337 * @param cbUdi the size of the buffer in @a pszUdi
338 *
339 * @note Used for more than DVDs these days.
340 */
341static void dvdCreateDeviceStrings(const char *pcszVendor, const char *pcszModel,
342 char *pszDesc, size_t cbDesc, char *pszUdi, size_t cbUdi) RT_NOEXCEPT
343{
344 AssertPtrReturnVoid(pcszVendor);
345 AssertPtrReturnVoid(pcszModel);
346 AssertPtrNullReturnVoid(pszDesc);
347 AssertReturnVoid(!pszDesc || cbDesc > 0);
348 AssertPtrNullReturnVoid(pszUdi);
349 AssertReturnVoid(!pszUdi || cbUdi > 0);
350
351 size_t cchVendor = strLenStripped(pcszVendor);
352 size_t cchModel = strLenStripped(pcszModel);
353
354 /* Create a cleaned version of the model string for the UDI string. */
355 char szCleaned[128];
356 for (unsigned i = 0; i < sizeof(szCleaned) && pcszModel[i] != '\0'; ++i)
357 if ( (pcszModel[i] >= '0' && pcszModel[i] <= '9')
358 || (pcszModel[i] >= 'A' && pcszModel[i] <= 'z'))
359 szCleaned[i] = pcszModel[i];
360 else
361 szCleaned[i] = '_';
362 szCleaned[RT_MIN(cchModel, sizeof(szCleaned) - 1)] = '\0';
363
364 /* Construct the description string as "Vendor Product" */
365 if (pszDesc)
366 {
367 if (cchVendor > 0)
368 {
369 RTStrPrintf(pszDesc, cbDesc, "%.*s %s", cchVendor, pcszVendor, cchModel > 0 ? pcszModel : "(unknown drive model)");
370 RTStrPurgeEncoding(pszDesc);
371 }
372 else
373 RTStrCopy(pszDesc, cbDesc, pcszModel);
374 }
375 /* Construct the UDI string */
376 if (pszUdi)
377 {
378 if (cchModel > 0)
379 RTStrPrintf(pszUdi, cbUdi, "/org/freedesktop/Hal/devices/storage_model_%s", szCleaned);
380 else
381 pszUdi[0] = '\0';
382 }
383}
384
385
386/**
387 * Check whether a device node points to a valid device and create a UDI and
388 * a description for it, and store the device number, if it does.
389 *
390 * @returns true if the device is valid, false otherwise
391 * @param pcszNode the path to the device node
392 * @param isDVD are we looking for a DVD device (or a floppy device)?
393 * @param pDevice where to store the device node (optional)
394 * @param pszDesc where to store the device description (optional)
395 * @param cbDesc the size of the buffer in @a pszDesc
396 * @param pszUdi where to store the device UDI (optional)
397 * @param cbUdi the size of the buffer in @a pszUdi
398 */
399static bool devValidateDevice(const char *pcszNode, bool isDVD, dev_t *pDevice,
400 char *pszDesc, size_t cbDesc, char *pszUdi, size_t cbUdi) RT_NOTHROW_DEF
401{
402 AssertPtrReturn(pcszNode, false);
403 AssertPtrNullReturn(pDevice, false);
404 AssertPtrNullReturn(pszDesc, false);
405 AssertReturn(!pszDesc || cbDesc > 0, false);
406 AssertPtrNullReturn(pszUdi, false);
407 AssertReturn(!pszUdi || cbUdi > 0, false);
408
409 RTFSOBJINFO ObjInfo;
410 if (RT_FAILURE(RTPathQueryInfo(pcszNode, &ObjInfo, RTFSOBJATTRADD_UNIX)))
411 return false;
412 if (!RTFS_IS_DEV_BLOCK(ObjInfo.Attr.fMode))
413 return false;
414 if (pDevice)
415 *pDevice = ObjInfo.Attr.u.Unix.Device;
416
417 if (isDVD)
418 {
419 char szVendor[128], szModel[128];
420 uint8_t u8Type;
421 if (!isCdromDevNum(ObjInfo.Attr.u.Unix.Device))
422 return false;
423 if (RT_FAILURE(cdromDoInquiry(pcszNode, &u8Type,
424 szVendor, sizeof(szVendor),
425 szModel, sizeof(szModel))))
426 return false;
427 if (u8Type != TYPE_ROM)
428 return false;
429 dvdCreateDeviceStrings(szVendor, szModel, pszDesc, cbDesc, pszUdi, cbUdi);
430 }
431 else
432 {
433 /* Floppies on Linux are legacy devices with hardcoded majors and minors */
434 if (major(ObjInfo.Attr.u.Unix.Device) != FLOPPY_MAJOR)
435 return false;
436
437 unsigned Number;
438 switch (minor(ObjInfo.Attr.u.Unix.Device))
439 {
440 case 0: case 1: case 2: case 3:
441 Number = minor(ObjInfo.Attr.u.Unix.Device);
442 break;
443 case 128: case 129: case 130: case 131:
444 Number = minor(ObjInfo.Attr.u.Unix.Device) - 128 + 4;
445 break;
446 default:
447 return false;
448 }
449
450 floppy_drive_name szName;
451 if (!floppyGetName(pcszNode, Number, szName))
452 return false;
453 floppyCreateDeviceStrings(szName, Number, pszDesc, cbDesc, pszUdi, cbUdi);
454 }
455 return true;
456}
457
458
459int VBoxMainDriveInfo::updateDVDs() RT_NOEXCEPT
460{
461 LogFlowThisFunc(("entered\n"));
462 int rc;
463 try
464 {
465 mDVDList.clear();
466 /* Always allow the user to override our auto-detection using an
467 * environment variable. */
468 bool fSuccess = false; /* Have we succeeded in finding anything yet? */
469 rc = getDriveInfoFromEnv("VBOX_CDROM", &mDVDList, true /* isDVD */, &fSuccess);
470 setNoProbe(false);
471 if (RT_SUCCESS(rc) && (!fSuccess || testing()))
472 rc = getDriveInfoFromSysfs(&mDVDList, DVD, &fSuccess);
473 if (RT_SUCCESS(rc) && testing())
474 {
475 setNoProbe(true);
476 rc = getDriveInfoFromSysfs(&mDVDList, DVD, &fSuccess);
477 }
478 }
479 catch (std::bad_alloc &e)
480 {
481 rc = VERR_NO_MEMORY;
482 }
483 LogFlowThisFunc(("rc=%Rrc\n", rc));
484 return rc;
485}
486
487int VBoxMainDriveInfo::updateFloppies() RT_NOEXCEPT
488{
489 LogFlowThisFunc(("entered\n"));
490 int rc;
491 try
492 {
493 mFloppyList.clear();
494 bool fSuccess = false; /* Have we succeeded in finding anything yet? */
495 rc = getDriveInfoFromEnv("VBOX_FLOPPY", &mFloppyList, false /* isDVD */, &fSuccess);
496 setNoProbe(false);
497 if (RT_SUCCESS(rc) && (!fSuccess || testing()))
498 rc = getDriveInfoFromSysfs(&mFloppyList, Floppy, &fSuccess);
499 if (RT_SUCCESS(rc) && testing())
500 {
501 setNoProbe(true);
502 rc = getDriveInfoFromSysfs(&mFloppyList, Floppy, &fSuccess);
503 }
504 }
505 catch (std::bad_alloc &)
506 {
507 rc = VERR_NO_MEMORY;
508 }
509 LogFlowThisFunc(("rc=%Rrc\n", rc));
510 return rc;
511}
512
513int VBoxMainDriveInfo::updateFixedDrives() RT_NOEXCEPT
514{
515 LogFlowThisFunc(("entered\n"));
516 int vrc;
517 try
518 {
519 mFixedDriveList.clear();
520 setNoProbe(false);
521 bool fSuccess = false; /* Have we succeeded in finding anything yet? */
522 vrc = getDriveInfoFromSysfs(&mFixedDriveList, FixedDisk, &fSuccess);
523 if (RT_SUCCESS(vrc) && testing())
524 {
525 setNoProbe(true);
526 vrc = getDriveInfoFromSysfs(&mFixedDriveList, FixedDisk, &fSuccess);
527 }
528 }
529 catch (std::bad_alloc &)
530 {
531 vrc = VERR_NO_MEMORY;
532 }
533 LogFlowThisFunc(("vrc=%Rrc\n", vrc));
534 return vrc;
535}
536
537
538/**
539 * Extract the names of drives from an environment variable and add them to a
540 * list if they are valid.
541 *
542 * @returns iprt status code
543 * @param pcszVar the name of the environment variable. The variable
544 * value should be a list of device node names, separated
545 * by ':' characters.
546 * @param pList the list to append the drives found to
547 * @param isDVD are we looking for DVD drives or for floppies?
548 * @param pfSuccess this will be set to true if we found at least one drive
549 * and to false otherwise. Optional.
550 *
551 * @note This is duplicated in HostHardwareFreeBSD.cpp.
552 */
553static int getDriveInfoFromEnv(const char *pcszVar, DriveInfoList *pList, bool isDVD, bool *pfSuccess) RT_NOTHROW_DEF
554{
555 AssertPtrReturn(pcszVar, VERR_INVALID_POINTER);
556 AssertPtrReturn(pList, VERR_INVALID_POINTER);
557 AssertPtrNullReturn(pfSuccess, VERR_INVALID_POINTER);
558 LogFlowFunc(("pcszVar=%s, pList=%p, isDVD=%d, pfSuccess=%p\n", pcszVar, pList, isDVD, pfSuccess));
559 int rc = VINF_SUCCESS;
560 bool success = false;
561 char *pszFreeMe = RTEnvDupEx(RTENV_DEFAULT, pcszVar);
562
563 try
564 {
565 char *pszCurrent = pszFreeMe;
566 while (pszCurrent && *pszCurrent != '\0')
567 {
568 char *pszNext = strchr(pszCurrent, ':');
569 if (pszNext)
570 *pszNext++ = '\0';
571
572 char szReal[RTPATH_MAX];
573 char szDesc[256], szUdi[256];
574 if ( RT_SUCCESS(RTPathReal(pszCurrent, szReal, sizeof(szReal)))
575 && devValidateDevice(szReal, isDVD, NULL, szDesc, sizeof(szDesc), szUdi, sizeof(szUdi)))
576 {
577 pList->push_back(DriveInfo(szReal, szUdi, szDesc));
578 success = true;
579 }
580 pszCurrent = pszNext;
581 }
582 if (pfSuccess != NULL)
583 *pfSuccess = success;
584 }
585 catch (std::bad_alloc &)
586 {
587 rc = VERR_NO_MEMORY;
588 }
589 RTStrFree(pszFreeMe);
590 LogFlowFunc(("rc=%Rrc, success=%d\n", rc, success));
591 return rc;
592}
593
594
595class SysfsBlockDev
596{
597public:
598 SysfsBlockDev(const char *pcszName, SysfsWantDevice_T wantDevice) RT_NOEXCEPT
599 : mpcszName(pcszName), mWantDevice(wantDevice), misConsistent(true), misValid(false)
600 {
601 if (findDeviceNode())
602 {
603 switch (mWantDevice)
604 {
605 case DVD: validateAndInitForDVD(); break;
606 case Floppy: validateAndInitForFloppy(); break;
607 default: validateAndInitForFixedDisk(); break;
608 }
609 }
610 }
611private:
612 /** The name of the subdirectory of /sys/block for this device */
613 const char *mpcszName;
614 /** Are we looking for a floppy, a DVD or a fixed disk device? */
615 SysfsWantDevice_T mWantDevice;
616 /** The device node for the device */
617 char mszNode[RTPATH_MAX];
618 /** Does the sysfs entry look like we expect it too? This is a canary
619 * for future sysfs ABI changes. */
620 bool misConsistent;
621 /** Is this entry a valid specimen of what we are looking for? */
622 bool misValid;
623 /** Human readable drive description string */
624 char mszDesc[256];
625 /** Unique identifier for the drive. Should be identical to hal's UDI for
626 * the device. May not be unique for two identical drives. */
627 char mszUdi[256];
628private:
629 /* Private methods */
630
631 /**
632 * Fill in the device node member based on the /sys/block subdirectory.
633 * @returns boolean success value
634 */
635 bool findDeviceNode() RT_NOEXCEPT
636 {
637 dev_t dev = 0;
638 int rc = RTLinuxSysFsReadDevNumFile(&dev, "block/%s/dev", mpcszName);
639 if (RT_FAILURE(rc) || dev == 0)
640 {
641 misConsistent = false;
642 return false;
643 }
644 rc = RTLinuxCheckDevicePath(dev, RTFS_TYPE_DEV_BLOCK, mszNode, sizeof(mszNode), "%s", mpcszName);
645 return RT_SUCCESS(rc);
646 }
647
648 /** Check whether the sysfs block entry is valid for a DVD device and
649 * initialise the string data members for the object. We try to get all
650 * the information we need from sysfs if possible, to avoid unnecessarily
651 * poking the device, and if that fails we fall back to an SCSI INQUIRY
652 * command. */
653 void validateAndInitForDVD() RT_NOEXCEPT
654 {
655 int64_t type = 0;
656 int rc = RTLinuxSysFsReadIntFile(10, &type, "block/%s/device/type", mpcszName);
657 if (RT_SUCCESS(rc) && type != TYPE_ROM)
658 return;
659 if (type == TYPE_ROM)
660 {
661 char szVendor[128];
662 rc = RTLinuxSysFsReadStrFile(szVendor, sizeof(szVendor), NULL, "block/%s/device/vendor", mpcszName);
663 if (RT_SUCCESS(rc))
664 {
665 char szModel[128];
666 rc = RTLinuxSysFsReadStrFile(szModel, sizeof(szModel), NULL, "block/%s/device/model", mpcszName);
667 if (RT_SUCCESS(rc))
668 {
669 misValid = true;
670 dvdCreateDeviceStrings(szVendor, szModel, mszDesc, sizeof(mszDesc), mszUdi, sizeof(mszUdi));
671 return;
672 }
673 }
674 }
675 if (!noProbe())
676 probeAndInitForDVD();
677 }
678
679 /** Try to find out whether a device is a DVD drive by sending it an
680 * SCSI INQUIRY command. If it is, initialise the string and validity
681 * data members for the object based on the returned data.
682 */
683 void probeAndInitForDVD() RT_NOEXCEPT
684 {
685 AssertReturnVoid(mszNode[0] != '\0');
686 uint8_t bType = 0;
687 char szVendor[128] = "";
688 char szModel[128] = "";
689 int rc = cdromDoInquiry(mszNode, &bType, szVendor, sizeof(szVendor), szModel, sizeof(szModel));
690 if (RT_SUCCESS(rc) && bType == TYPE_ROM)
691 {
692 misValid = true;
693 dvdCreateDeviceStrings(szVendor, szModel, mszDesc, sizeof(mszDesc), mszUdi, sizeof(mszUdi));
694 }
695 }
696
697 /** Check whether the sysfs block entry is valid for a floppy device and
698 * initialise the string data members for the object. Since we only
699 * support floppies using the basic "floppy" driver, we check the driver
700 * using the entry name and a driver-specific ioctl. */
701 void validateAndInitForFloppy() RT_NOEXCEPT
702 {
703 floppy_drive_name szName;
704 char szDriver[8];
705 if ( mpcszName[0] != 'f'
706 || mpcszName[1] != 'd'
707 || mpcszName[2] < '0'
708 || mpcszName[2] > '7'
709 || mpcszName[3] != '\0')
710 return;
711 bool fHaveName = false;
712 if (!noProbe())
713 fHaveName = floppyGetName(mszNode, mpcszName[2] - '0', szName);
714 int rc = RTLinuxSysFsGetLinkDest(szDriver, sizeof(szDriver), NULL, "block/%s/%s",
715 mpcszName, "device/driver");
716 if (RT_SUCCESS(rc))
717 {
718 if (RTStrCmp(szDriver, "floppy"))
719 return;
720 }
721 else if (!fHaveName)
722 return;
723 floppyCreateDeviceStrings(fHaveName ? szName : NULL,
724 mpcszName[2] - '0', mszDesc,
725 sizeof(mszDesc), mszUdi, sizeof(mszUdi));
726 misValid = true;
727 }
728
729 void validateAndInitForFixedDisk() RT_NOEXCEPT
730 {
731 /*
732 * For current task only device path is needed. Therefore, device probing
733 * is skipped and other fields are empty if there aren't files in the
734 * device entry.
735 */
736 int64_t type = 0;
737 int rc = RTLinuxSysFsReadIntFile(10, &type, "block/%s/device/type", mpcszName);
738 if (RT_SUCCESS(rc) && type != TYPE_DISK)
739 return;
740 char szVendor[128];
741 rc = RTLinuxSysFsReadStrFile(szVendor, sizeof(szVendor), NULL, "block/%s/device/vendor", mpcszName);
742 if (RT_SUCCESS(rc))
743 {
744 char szModel[128];
745 rc = RTLinuxSysFsReadStrFile(szModel, sizeof(szModel), NULL, "block/%s/device/model", mpcszName);
746 if (RT_SUCCESS(rc))
747 {
748 misValid = true;
749 dvdCreateDeviceStrings(szVendor, szModel, mszDesc, sizeof(mszDesc), mszUdi, sizeof(mszUdi));
750 return;
751 }
752 }
753 }
754
755public:
756 bool isConsistent() const RT_NOEXCEPT
757 {
758 return misConsistent;
759 }
760 bool isValid() const RT_NOEXCEPT
761 {
762 return misValid;
763 }
764 const char *getDesc() const RT_NOEXCEPT
765 {
766 return mszDesc;
767 }
768 const char *getUdi() const RT_NOEXCEPT
769 {
770 return mszUdi;
771 }
772 const char *getNode() const RT_NOEXCEPT
773 {
774 return mszNode;
775 }
776};
777
778/**
779 * Helper function to query the sysfs subsystem for information about DVD
780 * drives attached to the system.
781 * @returns iprt status code
782 * @param pList where to add information about the drives detected
783 * @param wantDevice The kind of devices we're looking for.
784 * @param pfSuccess Did we find anything?
785 *
786 * @returns IPRT status code
787 * @throws Nothing.
788 */
789static int getDriveInfoFromSysfs(DriveInfoList *pList, SysfsWantDevice_T wantDevice, bool *pfSuccess) RT_NOTHROW_DEF
790{
791 AssertPtrReturn(pList, VERR_INVALID_POINTER);
792 AssertPtrNullReturn(pfSuccess, VERR_INVALID_POINTER); /* Valid or Null */
793 LogFlowFunc (("pList=%p, wantDevice=%u, pfSuccess=%p\n",
794 pList, (unsigned)wantDevice, pfSuccess));
795 if (!RTPathExists("/sys"))
796 return VINF_SUCCESS;
797
798 bool fSuccess = true;
799 unsigned cFound = 0;
800 RTDIR hDir = NIL_RTDIR;
801 int rc = RTDirOpen(&hDir, "/sys/block");
802 /* This might mean that sysfs semantics have changed */
803 AssertReturn(rc != VERR_FILE_NOT_FOUND, VINF_SUCCESS);
804 if (RT_SUCCESS(rc))
805 {
806 for (;;)
807 {
808 RTDIRENTRY entry;
809 rc = RTDirRead(hDir, &entry, NULL);
810 Assert(rc != VERR_BUFFER_OVERFLOW); /* Should never happen... */
811 if (RT_FAILURE(rc)) /* Including overflow and no more files */
812 break;
813 if (entry.szName[0] == '.')
814 continue;
815 SysfsBlockDev dev(entry.szName, wantDevice);
816 /* This might mean that sysfs semantics have changed */
817 AssertBreakStmt(dev.isConsistent(), fSuccess = false);
818 if (!dev.isValid())
819 continue;
820 try
821 {
822 pList->push_back(DriveInfo(dev.getNode(), dev.getUdi(), dev.getDesc()));
823 }
824 catch (std::bad_alloc &e)
825 {
826 rc = VERR_NO_MEMORY;
827 break;
828 }
829 ++cFound;
830 }
831 RTDirClose(hDir);
832 }
833 if (rc == VERR_NO_MORE_FILES)
834 rc = VINF_SUCCESS;
835 else if (RT_FAILURE(rc))
836 /* Clean up again */
837 while (cFound-- > 0)
838 pList->pop_back();
839 if (pfSuccess)
840 *pfSuccess = fSuccess;
841 LogFlow (("rc=%Rrc, fSuccess=%u\n", rc, (unsigned)fSuccess));
842 return rc;
843}
844
845
846/** Helper for readFilePathsFromDir(). Adds a path to the vector if it is not
847 * NULL and not a dotfile (".", "..", ".*"). */
848static int maybeAddPathToVector(const char *pcszPath, const char *pcszEntry, VECTOR_PTR(char *) *pvecpchDevs) RT_NOTHROW_DEF
849{
850 if (!pcszPath)
851 return 0;
852 if (pcszEntry[0] == '.')
853 return 0;
854 char *pszPath = RTStrDup(pcszPath);
855 if (pszPath)
856 {
857 int vrc = VEC_PUSH_BACK_PTR(pvecpchDevs, char *, pszPath);
858 if (RT_SUCCESS(vrc))
859 return 0;
860 }
861 return ENOMEM;
862}
863
864/**
865 * Helper for readFilePaths().
866 *
867 * Adds the entries from the open directory @a pDir to the vector @a pvecpchDevs
868 * using either the full path or the realpath() and skipping hidden files
869 * and files on which realpath() fails.
870 */
871static int readFilePathsFromDir(const char *pcszPath, DIR *pDir, VECTOR_PTR(char *) *pvecpchDevs, int withRealPath) RT_NOTHROW_DEF
872{
873 struct dirent entry, *pResult;
874 int err;
875
876#if RT_GNUC_PREREQ(4, 6)
877# pragma GCC diagnostic push
878# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
879#endif
880 for (err = readdir_r(pDir, &entry, &pResult);
881 pResult /** @todo r=bird: && err == 0 ? */;
882 err = readdir_r(pDir, &entry, &pResult))
883#if RT_GNUC_PREREQ(4, 6)
884# pragma GCC diagnostic pop
885#endif
886 {
887 /* We (implicitly) require that PATH_MAX be defined */
888 char szPath[PATH_MAX + 1], szRealPath[PATH_MAX + 1], *pszPath;
889 if (snprintf(szPath, sizeof(szPath), "%s/%s", pcszPath,
890 entry.d_name) < 0)
891 return errno;
892 if (withRealPath)
893 pszPath = realpath(szPath, szRealPath);
894 else
895 pszPath = szPath;
896 if ((err = maybeAddPathToVector(pszPath, entry.d_name, pvecpchDevs)))
897 return err;
898 }
899 return err;
900}
901
902
903/**
904 * Helper for walkDirectory to dump the names of a directory's entries into a
905 * vector of char pointers.
906 *
907 * @returns zero on success or (positive) posix error value.
908 * @param pcszPath the path to dump.
909 * @param pvecpchDevs an empty vector of char pointers - must be cleaned up
910 * by the caller even on failure.
911 * @param withRealPath whether to canonicalise the filename with realpath
912 */
913static int readFilePaths(const char *pcszPath, VECTOR_PTR(char *) *pvecpchDevs, int withRealPath) RT_NOTHROW_DEF
914{
915 AssertPtrReturn(pvecpchDevs, EINVAL);
916 AssertReturn(VEC_SIZE_PTR(pvecpchDevs) == 0, EINVAL);
917 AssertPtrReturn(pcszPath, EINVAL);
918
919 DIR *pDir = opendir(pcszPath);
920 if (!pDir)
921 return RTErrConvertFromErrno(errno);
922 int err = readFilePathsFromDir(pcszPath, pDir, pvecpchDevs, withRealPath);
923 if (closedir(pDir) < 0 && !err)
924 err = errno;
925 return RTErrConvertFromErrno(err);
926}
927
928
929class hotplugNullImpl : public VBoxMainHotplugWaiterImpl
930{
931public:
932 hotplugNullImpl(const char *) {}
933 virtual ~hotplugNullImpl (void) {}
934 /** @copydoc VBoxMainHotplugWaiter::Wait */
935 virtual int Wait (RTMSINTERVAL cMillies)
936 {
937 NOREF(cMillies);
938 return VERR_NOT_SUPPORTED;
939 }
940 /** @copydoc VBoxMainHotplugWaiter::Interrupt */
941 virtual void Interrupt (void) {}
942 virtual int getStatus(void)
943 {
944 return VERR_NOT_SUPPORTED;
945 }
946
947};
948
949#ifdef VBOX_USB_WITH_SYSFS
950# ifdef VBOX_USB_WITH_INOTIFY
951/** Class wrapper around an inotify watch (or a group of them to be precise).
952 */
953typedef struct inotifyWatch
954{
955 /** Pointer to the inotify_add_watch() glibc function/Linux API */
956 int (*inotify_add_watch)(int, const char *, uint32_t);
957 /** The native handle of the inotify fd. */
958 int mhInotify;
959} inotifyWatch;
960
961/** The flags we pass to inotify - modify, create, delete, change permissions
962 */
963#define IN_FLAGS 0x306
964
965static int iwAddWatch(inotifyWatch *pSelf, const char *pcszPath)
966{
967 errno = 0;
968 if ( pSelf->inotify_add_watch(pSelf->mhInotify, pcszPath, IN_FLAGS) >= 0
969 || (errno == EACCES))
970 return VINF_SUCCESS;
971 /* Other errors listed in the manpage can be treated as fatal */
972 return RTErrConvertFromErrno(errno);
973}
974
975/** Object initialisation */
976static int iwInit(inotifyWatch *pSelf)
977{
978 int (*inotify_init)(void);
979 int fd, flags;
980 int rc = VINF_SUCCESS;
981
982 AssertPtr(pSelf);
983 pSelf->mhInotify = -1;
984 errno = 0;
985 *(void **)(&inotify_init) = dlsym(RTLD_DEFAULT, "inotify_init");
986 if (!inotify_init)
987 return VERR_LDR_IMPORTED_SYMBOL_NOT_FOUND;
988 *(void **)(&pSelf->inotify_add_watch)
989 = dlsym(RTLD_DEFAULT, "inotify_add_watch");
990 if (!pSelf->inotify_add_watch)
991 return VERR_LDR_IMPORTED_SYMBOL_NOT_FOUND;
992 fd = inotify_init();
993 if (fd < 0)
994 {
995 Assert(errno > 0);
996 return RTErrConvertFromErrno(errno);
997 }
998 Assert(errno == 0);
999
1000 flags = fcntl(fd, F_GETFL, NULL);
1001 if ( flags < 0
1002 || fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0
1003 || fcntl(fd, F_SETFD, FD_CLOEXEC) < 0 /* race here */)
1004 {
1005 Assert(errno > 0);
1006 rc = RTErrConvertFromErrno(errno);
1007 }
1008 if (RT_FAILURE(rc))
1009 close(fd);
1010 else
1011 {
1012 Assert(errno == 0);
1013 pSelf->mhInotify = fd;
1014 }
1015 return rc;
1016}
1017
1018static void iwTerm(inotifyWatch *pSelf)
1019{
1020 AssertPtrReturnVoid(pSelf);
1021 if (pSelf->mhInotify != -1)
1022 {
1023 close(pSelf->mhInotify);
1024 pSelf->mhInotify = -1;
1025 }
1026}
1027
1028static int iwGetFD(inotifyWatch *pSelf)
1029{
1030 AssertPtrReturn(pSelf, -1);
1031 return pSelf->mhInotify;
1032}
1033
1034# define SYSFS_WAKEUP_STRING "Wake up!"
1035
1036class hotplugInotifyImpl : public VBoxMainHotplugWaiterImpl
1037{
1038 /** Pipe used to interrupt wait(), the read end. */
1039 int mhWakeupPipeR;
1040 /** Pipe used to interrupt wait(), the write end. */
1041 int mhWakeupPipeW;
1042 /** The inotify watch set */
1043 inotifyWatch mWatches;
1044 /** Flag to mark that the Wait() method is currently being called, and to
1045 * ensure that it isn't called multiple times in parallel. */
1046 volatile uint32_t mfWaiting;
1047 /** The root of the USB devices tree. */
1048 const char *mpcszDevicesRoot;
1049 /** iprt result code from object initialisation. Should be AssertReturn-ed
1050 * on at the start of all methods. I went this way because I didn't want
1051 * to deal with exceptions. */
1052 int mStatus;
1053 /** ID values associates with the wakeup pipe and the FAM socket for polling
1054 */
1055 enum
1056 {
1057 RPIPE_ID = 0,
1058 INOTIFY_ID,
1059 MAX_POLLID
1060 };
1061
1062 /** Clean up any resources in use, gracefully skipping over any which have
1063 * not yet been allocated or already cleaned up. Intended to be called
1064 * from the destructor or after a failed initialisation. */
1065 void term(void);
1066
1067 int drainInotify();
1068
1069 /** Read the wakeup string from the wakeup pipe */
1070 int drainWakeupPipe(void);
1071public:
1072 hotplugInotifyImpl(const char *pcszDevicesRoot);
1073 virtual ~hotplugInotifyImpl(void)
1074 {
1075 term();
1076#ifdef DEBUG
1077 /** The first call to term should mark all resources as freed, so this
1078 * should be a semantic no-op. */
1079 term();
1080#endif
1081 }
1082 /** Is inotify available and working on this system? If so we expect that
1083 * this implementation will be usable. */
1084 /** @todo test the "inotify in glibc but not in the kernel" case. */
1085 static bool Available(void)
1086 {
1087 int (*inotify_init)(void);
1088
1089 *(void **)(&inotify_init) = dlsym(RTLD_DEFAULT, "inotify_init");
1090 if (!inotify_init)
1091 return false;
1092 int fd = inotify_init();
1093 if (fd == -1)
1094 return false;
1095 close(fd);
1096 return true;
1097 }
1098
1099 virtual int getStatus(void)
1100 {
1101 return mStatus;
1102 }
1103
1104 /** @copydoc VBoxMainHotplugWaiter::Wait */
1105 virtual int Wait(RTMSINTERVAL);
1106 /** @copydoc VBoxMainHotplugWaiter::Interrupt */
1107 virtual void Interrupt(void);
1108};
1109
1110/** Simplified version of RTPipeCreate */
1111static int pipeCreateSimple(int *phPipeRead, int *phPipeWrite)
1112{
1113 AssertPtrReturn(phPipeRead, VERR_INVALID_POINTER);
1114 AssertPtrReturn(phPipeWrite, VERR_INVALID_POINTER);
1115
1116 /*
1117 * Create the pipe and set the close-on-exec flag.
1118 */
1119 int aFds[2] = {-1, -1};
1120 if (pipe(aFds))
1121 return RTErrConvertFromErrno(errno);
1122 if ( fcntl(aFds[0], F_SETFD, FD_CLOEXEC) < 0
1123 || fcntl(aFds[1], F_SETFD, FD_CLOEXEC) < 0)
1124 {
1125 int rc = RTErrConvertFromErrno(errno);
1126 close(aFds[0]);
1127 close(aFds[1]);
1128 return rc;
1129 }
1130
1131 *phPipeRead = aFds[0];
1132 *phPipeWrite = aFds[1];
1133
1134 /*
1135 * Before we leave, make sure to shut up SIGPIPE.
1136 */
1137 signal(SIGPIPE, SIG_IGN);
1138 return VINF_SUCCESS;
1139}
1140
1141hotplugInotifyImpl::hotplugInotifyImpl(const char *pcszDevicesRoot) :
1142 mhWakeupPipeR(-1), mhWakeupPipeW(-1), mfWaiting(0),
1143 mpcszDevicesRoot(pcszDevicesRoot), mStatus(VERR_WRONG_ORDER)
1144{
1145# ifdef DEBUG
1146 /* Excercise the code path (term() on a not-fully-initialised object) as
1147 * well as we can. On an uninitialised object this method is a semantic
1148 * no-op. */
1149 mWatches.mhInotify = -1; /* term will access this variable */
1150 term();
1151 /* For now this probing method should only be used if nothing else is
1152 * available */
1153# endif
1154 int rc;
1155 do {
1156 if (RT_FAILURE(rc = iwInit(&mWatches)))
1157 break;
1158 if (RT_FAILURE(rc = iwAddWatch(&mWatches, mpcszDevicesRoot)))
1159 break;
1160 if (RT_FAILURE(rc = pipeCreateSimple(&mhWakeupPipeR, &mhWakeupPipeW)))
1161 break;
1162 } while (0);
1163 mStatus = rc;
1164 if (RT_FAILURE(rc))
1165 term();
1166}
1167
1168void hotplugInotifyImpl::term(void)
1169{
1170 /** This would probably be a pending segfault, so die cleanly */
1171 AssertRelease(!mfWaiting);
1172 if (mhWakeupPipeR != -1)
1173 {
1174 close(mhWakeupPipeR);
1175 mhWakeupPipeR = -1;
1176 }
1177 if (mhWakeupPipeW != -1)
1178 {
1179 close(mhWakeupPipeW);
1180 mhWakeupPipeW = -1;
1181 }
1182 iwTerm(&mWatches);
1183}
1184
1185int hotplugInotifyImpl::drainInotify()
1186{
1187 char chBuf[RTPATH_MAX + 256]; /* Should always be big enough */
1188 ssize_t cchRead;
1189
1190 AssertRCReturn(mStatus, VERR_WRONG_ORDER);
1191 errno = 0;
1192 do {
1193 cchRead = read(iwGetFD(&mWatches), chBuf, sizeof(chBuf));
1194 } while (cchRead > 0);
1195 if (cchRead == 0)
1196 return VINF_SUCCESS;
1197 if ( cchRead < 0
1198 && ( errno == EAGAIN
1199#if EAGAIN != EWOULDBLOCK
1200 || errno == EWOULDBLOCK
1201#endif
1202 ))
1203 return VINF_SUCCESS;
1204 Assert(errno > 0);
1205 return RTErrConvertFromErrno(errno);
1206}
1207
1208int hotplugInotifyImpl::drainWakeupPipe(void)
1209{
1210 char szBuf[sizeof(SYSFS_WAKEUP_STRING)];
1211 ssize_t cbRead;
1212
1213 AssertRCReturn(mStatus, VERR_WRONG_ORDER);
1214 cbRead = read(mhWakeupPipeR, szBuf, sizeof(szBuf));
1215 Assert(cbRead > 0);
1216 NOREF(cbRead);
1217 return VINF_SUCCESS;
1218}
1219
1220int hotplugInotifyImpl::Wait(RTMSINTERVAL aMillies)
1221{
1222 int rc;
1223 char **ppszEntry;
1224 VECTOR_PTR(char *) vecpchDevs;
1225
1226 AssertRCReturn(mStatus, VERR_WRONG_ORDER);
1227 bool fEntered = ASMAtomicCmpXchgU32(&mfWaiting, 1, 0);
1228 AssertReturn(fEntered, VERR_WRONG_ORDER);
1229 VEC_INIT_PTR(&vecpchDevs, char *, RTStrFree);
1230 do {
1231 struct pollfd pollFD[MAX_POLLID];
1232
1233 rc = readFilePaths(mpcszDevicesRoot, &vecpchDevs, false);
1234 if (RT_SUCCESS(rc))
1235 VEC_FOR_EACH(&vecpchDevs, char *, ppszEntry)
1236 if (RT_FAILURE(rc = iwAddWatch(&mWatches, *ppszEntry)))
1237 break;
1238 if (RT_FAILURE(rc))
1239 break;
1240 pollFD[RPIPE_ID].fd = mhWakeupPipeR;
1241 pollFD[RPIPE_ID].events = POLLIN;
1242 pollFD[INOTIFY_ID].fd = iwGetFD(&mWatches);
1243 pollFD[INOTIFY_ID].events = POLLIN | POLLERR | POLLHUP;
1244 errno = 0;
1245 int cPolled = poll(pollFD, RT_ELEMENTS(pollFD), aMillies);
1246 if (cPolled < 0)
1247 {
1248 Assert(errno > 0);
1249 rc = RTErrConvertFromErrno(errno);
1250 }
1251 else if (pollFD[RPIPE_ID].revents)
1252 {
1253 rc = drainWakeupPipe();
1254 if (RT_SUCCESS(rc))
1255 rc = VERR_INTERRUPTED;
1256 break;
1257 }
1258 else if (!(pollFD[INOTIFY_ID].revents))
1259 {
1260 AssertBreakStmt(cPolled == 0, rc = VERR_INTERNAL_ERROR);
1261 rc = VERR_TIMEOUT;
1262 }
1263 Assert(errno == 0 || (RT_FAILURE(rc) && rc != VERR_TIMEOUT));
1264 if (RT_FAILURE(rc))
1265 break;
1266 AssertBreakStmt(cPolled == 1, rc = VERR_INTERNAL_ERROR);
1267 if (RT_FAILURE(rc = drainInotify()))
1268 break;
1269 } while (false);
1270 mfWaiting = 0;
1271 VEC_CLEANUP_PTR(&vecpchDevs);
1272 return rc;
1273}
1274
1275void hotplugInotifyImpl::Interrupt(void)
1276{
1277 AssertRCReturnVoid(mStatus);
1278 ssize_t cbWritten = write(mhWakeupPipeW, SYSFS_WAKEUP_STRING,
1279 sizeof(SYSFS_WAKEUP_STRING));
1280 if (cbWritten > 0)
1281 fsync(mhWakeupPipeW);
1282}
1283
1284# endif /* VBOX_USB_WITH_INOTIFY */
1285#endif /* VBOX_USB_WTH_SYSFS */
1286
1287VBoxMainHotplugWaiter::VBoxMainHotplugWaiter(const char *pcszDevicesRoot)
1288{
1289 try
1290 {
1291#ifdef VBOX_USB_WITH_SYSFS
1292# ifdef VBOX_USB_WITH_INOTIFY
1293 if (hotplugInotifyImpl::Available())
1294 {
1295 mImpl = new hotplugInotifyImpl(pcszDevicesRoot);
1296 return;
1297 }
1298# endif /* VBOX_USB_WITH_INOTIFY */
1299#endif /* VBOX_USB_WITH_SYSFS */
1300 mImpl = new hotplugNullImpl(pcszDevicesRoot);
1301 }
1302 catch (std::bad_alloc &e)
1303 { }
1304}
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