Changeset 23310 in vbox
- Timestamp:
- Sep 24, 2009 7:48:06 PM (15 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Main/linux/HostHardwareLinux.cpp
r21878 r23310 32 32 #include <VBox/log.h> 33 33 34 #include <iprt/dir.h> 34 35 #include <iprt/env.h> 36 #include <iprt/file.h> 35 37 #include <iprt/mem.h> 38 #include <iprt/param.h> 39 #include <iprt/thread.h> /* for RTThreadSleep() */ 36 40 #include <iprt/string.h> 37 41 … … 53 57 # endif 54 58 # include <errno.h> 59 # include <scsi/scsi.h> 60 # include <scsi/sg.h> 61 62 # include <iprt/linux/sysfs.h> 55 63 #endif /* RT_OS_LINUX */ 56 64 #include <vector> … … 75 83 static int getDriveInfoFromEnv(const char *pszVar, DriveInfoList *pList, 76 84 bool isDVD, bool *pfSuccess); 85 static int getDriveInfoFromSysfs(DriveInfoList *pList, bool isDVD, 86 bool *pfSuccess); 87 int scsiDoInquiry(const char *pszNode, uint8_t *pu8Type, char *pchVendor, 88 size_t cchVendor, char *pchModel, size_t cchModel); 77 89 static int getDVDInfoFromMTab(char *mountTable, DriveInfoList *pList); 78 90 #ifdef VBOX_WITH_DBUS … … 116 128 #endif /* VBOX_WITH_DBUS */ 117 129 130 /** Find the length of a string, ignoring trailing non-ascii or control 131 * characters */ 132 static 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 118 141 int VBoxMainDriveInfo::updateDVDs () 119 142 { … … 134 157 rc = getDriveInfoFromHal(&mDVDList, true /* isDVD */, &success); 135 158 #endif /* VBOX_WITH_DBUS defined */ 159 if (RT_SUCCESS(rc) && (!success | testing())) 160 rc = getDriveInfoFromSysfs(&mDVDList, true /* isDVD */, &success); 136 161 /* On Linux without hal, the situation is much more complex. We will 137 162 * take a heuristical approach. The general strategy is to try some … … 180 205 rc = getDriveInfoFromHal(&mFloppyList, false /* isDVD */, &success); 181 206 #endif /* VBOX_WITH_DBUS defined */ 207 if ( RT_SUCCESS(rc) 208 && RT_SUCCESS(VBoxLoadDBusLib()) 209 && (!success || testing())) 210 rc = getDriveInfoFromHal(&mFloppyList, false /* isDVD */, &success); 182 211 /* As with the CDROMs, on Linux we have to take a multi-level approach 183 212 * involving parsing the mount tables. As this is not bulletproof, we … … 1046 1075 1047 1076 /** 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 */ 1091 int 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 1154 class sysfsBlockDev 1155 { 1156 public: 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 } 1168 private: 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]; 1182 private: 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 1330 public: 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 */ 1360 int 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 /** 1048 1412 * Helper function to query the hal subsystem for information about drives 1049 1413 * attached to the system.
Note:
See TracChangeset
for help on using the changeset viewer.