VirtualBox

Changeset 23310 in vbox


Ignore:
Timestamp:
Sep 24, 2009 7:48:06 PM (15 years ago)
Author:
vboxsync
Message:

Main/HostHardwareLinux: use sysfs for querying host drives if hal is not available

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Main/linux/HostHardwareLinux.cpp

    r21878 r23310  
    3232#include <VBox/log.h>
    3333
     34#include <iprt/dir.h>
    3435#include <iprt/env.h>
     36#include <iprt/file.h>
    3537#include <iprt/mem.h>
     38#include <iprt/param.h>
     39#include <iprt/thread.h>  /* for RTThreadSleep() */
    3640#include <iprt/string.h>
    3741
     
    5357# endif
    5458# include <errno.h>
     59# include <scsi/scsi.h>
     60# include <scsi/sg.h>
     61
     62# include <iprt/linux/sysfs.h>
    5563#endif /* RT_OS_LINUX */
    5664#include <vector>
     
    7583static int getDriveInfoFromEnv(const char *pszVar, DriveInfoList *pList,
    7684                               bool isDVD, bool *pfSuccess);
     85static int getDriveInfoFromSysfs(DriveInfoList *pList, bool isDVD,
     86                                 bool *pfSuccess);
     87int scsiDoInquiry(const char *pszNode, uint8_t *pu8Type, char *pchVendor,
     88                  size_t cchVendor, char *pchModel, size_t cchModel);
    7789static int getDVDInfoFromMTab(char *mountTable, DriveInfoList *pList);
    7890#ifdef VBOX_WITH_DBUS
     
    116128#endif  /* VBOX_WITH_DBUS */
    117129
     130/** Find the length of a string, ignoring trailing non-ascii or control
     131 * characters */
     132static size_t strLenStripped(const char *psz)
     133{
     134    size_t cch = 0;
     135    for (size_t i = 0; psz[i] != '\0'; ++i)
     136        if (psz[i] > 32 && psz[i] < 127)
     137            cch = i;
     138    return cch + 1;
     139}
     140
    118141int VBoxMainDriveInfo::updateDVDs ()
    119142{
     
    134157            rc = getDriveInfoFromHal(&mDVDList, true /* isDVD */, &success);
    135158#endif /* VBOX_WITH_DBUS defined */
     159        if (RT_SUCCESS(rc) && (!success | testing()))
     160            rc = getDriveInfoFromSysfs(&mDVDList, true /* isDVD */, &success);
    136161        /* On Linux without hal, the situation is much more complex. We will
    137162         * take a heuristical approach.  The general strategy is to try some
     
    180205            rc = getDriveInfoFromHal(&mFloppyList, false /* isDVD */, &success);
    181206#endif /* VBOX_WITH_DBUS defined */
     207        if (   RT_SUCCESS(rc)
     208            && RT_SUCCESS(VBoxLoadDBusLib())
     209            && (!success || testing()))
     210            rc = getDriveInfoFromHal(&mFloppyList, false /* isDVD */, &success);
    182211        /* As with the CDROMs, on Linux we have to take a multi-level approach
    183212         * involving parsing the mount tables. As this is not bulletproof, we
     
    10461075
    10471076/**
     1077 * Send an SCSI INQUIRY command to a device and return selected information.
     1078 * @returns  iprt status code
     1079 * @returns  VERR_TRY_AGAIN if the query failed but might succeed next time
     1080 * @param pszNode    the full path to the device node
     1081 * @param pu8Type    where to store the SCSI device type on success (optional)
     1082 * @param pchVendor  where to store the vendor id string on success (optional)
     1083 * @param cchVendor  the size of the @a pchVendor buffer
     1084 * @param pchModel   where to store the product id string on success (optional)
     1085 * @param cchModel   the size of the @a pchModel buffer
     1086 * @note check documentation on the SCSI INQUIRY command and the Linux kernel
     1087 *       SCSI headers included above if you want to understand what is going
     1088 *       on in this method.
     1089 */
     1090/* static */
     1091int scsiDoInquiry(const char *pszNode, uint8_t *pu8Type, char *pchVendor,
     1092                  size_t cchVendor, char *pchModel, size_t cchModel)
     1093{
     1094    LogRelFlowFunc(("pszNode=%s, pu8Type=%p, pchVendor=%p, cchVendor=%llu, pchModel=%p, cchModel=%llu\n",
     1095                    pszNode, pu8Type, pchVendor, cchVendor, pchModel,
     1096                    cchModel));
     1097    AssertPtrReturn(pszNode, VERR_INVALID_POINTER);
     1098    AssertPtrNullReturn(pu8Type, VERR_INVALID_POINTER);
     1099    AssertPtrNullReturn(pchVendor, VERR_INVALID_POINTER);
     1100    AssertPtrNullReturn(pchModel, VERR_INVALID_POINTER);
     1101
     1102    sg_io_hdr_t ScsiIoReq = {0};
     1103    unsigned char u8Response[96] = { 0 };
     1104    unsigned char u8Command[6] =
     1105        { INQUIRY, 0, 0, 0, sizeof(u8Response), 0 };  /* INQUIRY */
     1106    int rc, rcIoCtl = 0;
     1107    RTFILE file;
     1108    rc = RTFileOpen(&file, pszNode, RTFILE_O_READ);
     1109    if (RT_SUCCESS(rc))
     1110    {
     1111        ScsiIoReq.interface_id = 'S';
     1112        ScsiIoReq.dxfer_direction = SG_DXFER_FROM_DEV;
     1113        ScsiIoReq.cmd_len = sizeof(u8Command);
     1114        ScsiIoReq.dxfer_len = sizeof(u8Response);
     1115        ScsiIoReq.dxferp = u8Response;
     1116        ScsiIoReq.cmdp = u8Command;
     1117        ScsiIoReq.timeout = 5000 /* ms */;
     1118        rc = RTFileIoCtl(file, SG_IO, &ScsiIoReq, 0, &rcIoCtl);
     1119        if (RT_SUCCESS(rc) && rcIoCtl < 0)
     1120            rc = VERR_NOT_SUPPORTED;
     1121        RTFileClose(file);
     1122    }
     1123    if (RT_SUCCESS(rc))
     1124    {
     1125        if (   (ScsiIoReq.status >> 1 == GOOD)
     1126            || (ScsiIoReq.status >> 1 == INTERMEDIATE_GOOD)
     1127            || (ScsiIoReq.status >> 1 == INTERMEDIATE_C_GOOD)
     1128            || (ScsiIoReq.status >> 1 == COMMAND_TERMINATED))
     1129        {
     1130            if (pu8Type)
     1131                *pu8Type = u8Response[0] & 0x1f;
     1132            if (pchVendor)
     1133                RTStrPrintf(pchVendor, cchVendor, "%.8s",
     1134                            (char *) &u8Response[8] /* vendor id string */);
     1135            if (pchModel)
     1136                RTStrPrintf(pchModel, cchModel, "%.16s",
     1137                            (char *) &u8Response[16] /* product id string */);
     1138        }
     1139        else if (   ScsiIoReq.status >> 1 != BUSY
     1140                 && ScsiIoReq.status >> 1 != QUEUE_FULL
     1141                 && ScsiIoReq.status >> 1 != CHECK_CONDITION)
     1142            rc = VERR_DEV_IO_ERROR;  /* Actually, these should never happen */
     1143        else
     1144            rc = VERR_TRY_AGAIN;
     1145    }
     1146    LogRelFlowFunc(("returning %Rrc\n", rc));
     1147    if (RT_SUCCESS(rc))
     1148        LogRelFlowFunc(("    type=%u, vendor=%.8s, product=%.16s\n",
     1149                        u8Response[0] & 0x1f, (char *) &u8Response[8],
     1150                        (char *) &u8Response[16]));
     1151    return rc;
     1152}
     1153
     1154class sysfsBlockDev
     1155{
     1156public:
     1157    sysfsBlockDev(const char *pcszName, bool wantDVD)
     1158            : mpcszName(pcszName), mwantDVD(wantDVD), misValid(false)
     1159    {
     1160        if (findDeviceNode())
     1161        {
     1162            if (mwantDVD)
     1163                validateAndInitForDVD();
     1164            else
     1165                validateAndInitForFloppy();
     1166        }
     1167    }
     1168private:
     1169    /** The name of the subdirectory of /sys/block for this device */
     1170    const char *mpcszName;
     1171    /** Are we looking for a floppy or a DVD device? */
     1172    bool mwantDVD;
     1173    /** The device node for the device */
     1174    char mszNode[RTPATH_MAX];
     1175    /** Is this entry a valid specimen of what we are looking for? */
     1176    bool misValid;
     1177    /** Human readible drive description string */
     1178    char mszDesc[256];
     1179    /** Unique identifier for the drive.  Should be identical to hal's UDI for
     1180     * the device.  May not be unique for two identical drives. */
     1181    char mszUdi[256];
     1182private:
     1183    /* Private methods */
     1184
     1185    /**
     1186     * Fill in the device node member based on the /sys/block subdirectory.
     1187     * @returns boolean success value
     1188     */
     1189    bool findDeviceNode()
     1190    {
     1191        dev_t dev = RTLinuxSysFsReadDevNumFile("block/%s/dev", mpcszName);
     1192        if (dev == 0)
     1193            return false;
     1194        if (RTLinuxFindDevicePath(dev, RTFS_TYPE_DEV_BLOCK, mszNode,
     1195                                  sizeof(mszNode), "%s", mpcszName) < 0)
     1196            return false;
     1197        return true;
     1198    }
     1199
     1200    /** Check whether the sysfs block entry is valid for a DVD device and
     1201     * initialise the string data members for the object.  We try to get all
     1202     * the information we need from sysfs if possible, to avoid unnecessarily
     1203     * poking the device, and if that fails we fall back to an SCSI INQUIRY
     1204     * command. */
     1205    void validateAndInitForDVD()
     1206    {
     1207        char szVendor[128], szModel[128];
     1208        ssize_t cchVendor, cchModel;
     1209        int64_t type = RTLinuxSysFsReadIntFile(10, "block/%s/device/type",
     1210                                               mpcszName);
     1211        if (type >= 0 && type != TYPE_ROM)
     1212            return;
     1213        if (type == 5)
     1214        {
     1215            cchVendor = RTLinuxSysFsReadStrFile(szVendor, sizeof(szVendor),
     1216                                                "block/%s/device/vendor",
     1217                                                mpcszName);
     1218            if (cchVendor >= 0)
     1219            {
     1220                cchModel = RTLinuxSysFsReadStrFile(szModel, sizeof(szModel),
     1221                                                   "block/%s/device/model",
     1222                                                   mpcszName);
     1223                if (cchModel >= 0)
     1224                {
     1225                    misValid = true;
     1226                    setDeviceStrings(szVendor, szModel);
     1227                    return;
     1228                }
     1229            }
     1230        }
     1231        probeAndInitForDVD();
     1232    }
     1233
     1234    /** Try to find out whether a device is a DVD drive by sending it an
     1235     * SCSI INQUIRY command.  If it is, initialise the string and validity
     1236     * data members for the object based on the returned data.
     1237     */
     1238    void probeAndInitForDVD()
     1239    {
     1240        AssertReturnVoid(mszNode[0] != '\0');
     1241        uint8_t u8Type = 0;
     1242        char szVendor[128] = "";
     1243        char szModel[128] = "";
     1244        for (unsigned i = 0; i < 5; ++i)  /* Give the device five chances */
     1245        {
     1246            int rc = scsiDoInquiry(mszNode, &u8Type, szVendor,
     1247                                   sizeof(szVendor), szModel,
     1248                                   sizeof(szModel));
     1249            if (RT_SUCCESS(rc))
     1250            {
     1251                if (u8Type != TYPE_ROM)
     1252                    return;
     1253                misValid = true;
     1254                setDeviceStrings(szVendor, szModel);
     1255                return;
     1256            }
     1257            if (rc != VERR_TRY_AGAIN)
     1258                return;
     1259            RTThreadSleep(100);  /* wait a little before retrying */
     1260        }
     1261    }
     1262
     1263    /**
     1264     * Initialise the object device strings (description and UDI) based on
     1265     * vendor and model name strings.
     1266     * @param pszVendor  the vendor ID string
     1267     * @param pszModel   the product ID string
     1268     */
     1269    void setDeviceStrings(const char *pszVendor, const char *pszModel)
     1270    {
     1271        char szCleaned[128];
     1272        size_t cchVendor = strLenStripped(pszVendor);
     1273        size_t cchModel = strLenStripped(pszModel);
     1274
     1275        /* Create a cleaned version of the model string for the UDI string. */
     1276        for (unsigned i = 0; pszModel[i] != '\0' && i < sizeof(szCleaned); ++i)
     1277            if (   (pszModel[i] >= '0' && pszModel[i] <= '9')
     1278                || (pszModel[i] >= 'A' && pszModel[i] <= 'z'))
     1279                szCleaned[i] = pszModel[i];
     1280            else
     1281                szCleaned[i] = '_';
     1282        szCleaned[RT_MIN(cchModel, sizeof(szCleaned) - 1)] = '\0';
     1283
     1284        /* Construct the description string as "Vendor Product" */
     1285        if (cchVendor > 0)
     1286            RTStrPrintf(mszDesc, sizeof(mszDesc), "%.*s %s", cchVendor,
     1287                        pszVendor,
     1288                        cchModel > 0 ? pszModel : "(unknown drive model)");
     1289        else
     1290            RTStrPrintf(mszDesc, sizeof(mszDesc), "%s", pszModel);
     1291        /* Construct the UDI string */
     1292        if (cchModel)
     1293            RTStrPrintf(mszUdi, sizeof(mszUdi),
     1294                        "/org/freedesktop/Hal/devices/storage_model_%s",
     1295                        szCleaned);
     1296        else
     1297            mszUdi[0] = '\0';
     1298    }
     1299
     1300    /** Check whether the sysfs block entry is valid for a floppy device and
     1301     * initialise the string data members for the object.  Since we only
     1302     * support floppies using the basic "floppy" driver, we just check the
     1303     * entry name and the bus type ("platform"). */
     1304    void validateAndInitForFloppy()
     1305    {
     1306        char szBus[128];
     1307        if (   mpcszName[0] != 'f'
     1308            || mpcszName[1] != 'd'
     1309            || mpcszName[2] < '0'
     1310            || mpcszName[2] > '3'
     1311            || mpcszName[3] != '\0')
     1312            return;
     1313        ssize_t cchBus = RTLinuxSysFsGetLinkDest(szBus, sizeof(szBus),
     1314                                                 "block/%s/device/bus",
     1315                                                 mpcszName);
     1316        if (cchBus < 0)
     1317            return;
     1318        if (strcmp(szBus, "platform") != 0)
     1319            return;
     1320        misValid = true;
     1321        strcpy(mszDesc,   (mpcszName[2] == '0') ? "PC Floppy drive"
     1322                        : (mpcszName[2] == '1') ? "Second PC Floppy drive"
     1323                        : (mpcszName[2] == '2') ? "Third PC Floppy drive"
     1324                        : "Fourth PC Floppy drive");
     1325        RTStrPrintf(mszUdi, sizeof(mszUdi),
     1326                    "/org/freedesktop/Hal/devices/platform_floppy_%u_storage",
     1327                    mpcszName[2]);
     1328    }
     1329
     1330public:
     1331    bool isValid()
     1332    {
     1333        return misValid;
     1334    }
     1335    const char *getDesc()
     1336    {
     1337        return mszDesc;
     1338    }
     1339    const char *getUdi()
     1340    {
     1341        return mszUdi;
     1342    }
     1343    const char *getNode()
     1344    {
     1345        return mszNode;
     1346    }
     1347};
     1348
     1349/**
     1350 * Helper function to query the sysfs subsystem for information about DVD
     1351 * drives attached to the system.
     1352 * @returns iprt status code
     1353 * @param   pList      where to add information about the drives detected
     1354 * @param   isDVD      are we looking for DVDs or floppies?
     1355 * @param   pfSuccess  Did we find anything?
     1356 *
     1357 * @returns IPRT status code
     1358 */
     1359/* static */
     1360int getDriveInfoFromSysfs(DriveInfoList *pList, bool isDVD, bool *pfSuccess)
     1361{
     1362    AssertPtrReturn(pList, VERR_INVALID_POINTER);
     1363    AssertPtrNullReturn(pfSuccess, VERR_INVALID_POINTER); /* Valid or Null */
     1364    LogFlowFunc (("pList=%p, isDVD=%u, pfSuccess=%p\n",
     1365                  pList, (unsigned) isDVD, pfSuccess));
     1366    PRTDIR pDir = NULL;
     1367    RTDIRENTRY entry = {0};
     1368    int rc;
     1369    bool fSuccess;
     1370    unsigned cFound = 0;
     1371
     1372    rc = RTDirOpen(&pDir, "/sys/block");
     1373    if (RT_SUCCESS(rc))
     1374        while (true)
     1375        {
     1376            rc = RTDirRead(pDir, &entry, NULL);
     1377            Assert(rc != VERR_BUFFER_OVERFLOW);  /* Should never happen... */
     1378            if (RT_FAILURE(rc))  /* Including overflow and no more files */
     1379                break;
     1380            if (entry.szName[0] == '.')
     1381                continue;
     1382            sysfsBlockDev dev(entry.szName, isDVD);
     1383            if (!dev.isValid())
     1384                continue;
     1385            try
     1386            {
     1387                pList->push_back(DriveInfo(dev.getNode(), dev.getUdi(),
     1388                                           dev.getDesc()));
     1389            }
     1390            catch(std::bad_alloc &e)
     1391            {
     1392                rc = VERR_NO_MEMORY;
     1393                break;
     1394            }
     1395            ++cFound;
     1396        }
     1397    RTDirClose(pDir);
     1398    if (rc == VERR_NO_MORE_FILES)
     1399        rc = VINF_SUCCESS;
     1400    fSuccess = (RT_SUCCESS(rc) && cFound > 0);
     1401    if (!fSuccess)
     1402        /* Clean up again */
     1403        for (unsigned i = 0; i < cFound; ++i)
     1404            pList->pop_back();
     1405    if (pfSuccess)
     1406        *pfSuccess = fSuccess;
     1407    LogFlow (("rc=%Rrc, fSuccess=%u\n", rc, (unsigned) fSuccess));
     1408    return rc;
     1409}
     1410
     1411/**
    10481412 * Helper function to query the hal subsystem for information about drives
    10491413 * attached to the system.
Note: See TracChangeset for help on using the changeset viewer.

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