|
MythTV
0.26-pre
|
00001 // C header 00002 #include <fcntl.h> 00003 #include <unistd.h> 00004 #include <sys/types.h> 00005 #include <sys/stat.h> 00006 #include <sys/param.h> 00007 00008 // Qt Headers 00009 #include <QDir> 00010 #include <QFileInfo> 00011 #include <QFileInfoList> 00012 #include <QTextStream> 00013 00014 // MythTV headers 00015 #include "mythmedia.h" 00016 #include "mythconfig.h" 00017 #include "mythlogging.h" 00018 #include "mythmiscutil.h" 00019 #include "mythsystem.h" 00020 #include "exitcodes.h" 00021 00022 using namespace std; 00023 00024 #ifdef USING_MINGW 00025 # define O_NONBLOCK 0 00026 #endif 00027 00028 #define LOC QString("MythMediaDevice:") 00029 00030 static const QString PATHTO_PMOUNT("/usr/bin/pmount"); 00031 static const QString PATHTO_PUMOUNT("/usr/bin/pumount"); 00032 #if CONFIG_DARWIN 00033 static const QString PATHTO_MOUNT("/sbin/mount"); 00034 #else 00035 static const QString PATHTO_MOUNT("/bin/mount"); 00036 #endif 00037 static const QString PATHTO_UNMOUNT("/bin/umount"); 00038 static const QString PATHTO_MOUNTS("/proc/mounts"); 00039 00040 #if CONFIG_DARWIN 00041 # define USE_MOUNT_COMMAND 00042 #endif 00043 00044 const char* MythMediaDevice::MediaStatusStrings[] = 00045 { 00046 "MEDIASTAT_ERROR", 00047 "MEDIASTAT_UNKNOWN", 00048 "MEDIASTAT_UNPLUGGED", 00049 "MEDIASTAT_OPEN", 00050 "MEDIASTAT_NODISK", 00051 "MEDIASTAT_UNFORMATTED", 00052 "MEDIASTAT_USEABLE", 00053 "MEDIASTAT_NOTMOUNTED", 00054 "MEDIASTAT_MOUNTED" 00055 }; 00056 00057 const char* MythMediaDevice::MediaErrorStrings[] = 00058 { 00059 "MEDIAERR_OK", 00060 "MEDIAERR_FAILED", 00061 "MEDIAERR_UNSUPPORTED" 00062 }; 00063 00064 QEvent::Type MythMediaEvent::kEventType = 00065 (QEvent::Type) QEvent::registerEventType(); 00066 00067 MythMediaDevice::MythMediaDevice(QObject* par, const char* DevicePath, 00068 bool SuperMount, bool AllowEject) 00069 : QObject(par) 00070 { 00071 m_DevicePath = DevicePath; 00072 m_AllowEject = AllowEject; 00073 m_Locked = false; 00074 m_DeviceHandle = -1; 00075 m_SuperMount = SuperMount; 00076 m_Status = MEDIASTAT_UNKNOWN; 00077 m_MediaType = MEDIATYPE_UNKNOWN; 00078 m_RealDevice = getSymlinkTarget(m_DevicePath); 00079 } 00080 00081 bool MythMediaDevice::openDevice() 00082 { 00083 // Sanity check 00084 if (isDeviceOpen()) 00085 return true; 00086 00087 QByteArray dev = m_DevicePath.toLocal8Bit(); 00088 m_DeviceHandle = open(dev.constData(), O_RDONLY | O_NONBLOCK); 00089 00090 return isDeviceOpen(); 00091 } 00092 00093 bool MythMediaDevice::closeDevice() 00094 { 00095 // Sanity check 00096 if (!isDeviceOpen()) 00097 return true; 00098 00099 int ret = close(m_DeviceHandle); 00100 m_DeviceHandle = -1; 00101 00102 return (ret != -1) ? true : false; 00103 } 00104 00105 bool MythMediaDevice::isDeviceOpen() const 00106 { 00107 return (m_DeviceHandle >= 0) ? true : false; 00108 } 00109 00110 bool MythMediaDevice::performMountCmd(bool DoMount) 00111 { 00112 if (DoMount && isMounted()) 00113 { 00114 #ifdef Q_OS_MAC 00115 // Not an error - DiskArbitration has already mounted the device. 00116 // AddDevice calls mount() so onDeviceMounted() can get mediaType. 00117 onDeviceMounted(); 00118 #else 00119 LOG(VB_MEDIA, LOG_ERR, "MythMediaDevice::performMountCmd(true)" 00120 " - Logic Error? Device already mounted."); 00121 return true; 00122 #endif 00123 } 00124 00125 if (isDeviceOpen()) 00126 closeDevice(); 00127 00128 if (!m_SuperMount) 00129 { 00130 QString MountCommand; 00131 00132 // Build a command line for mount/unmount and execute it... 00133 // Is there a better way to do this? 00134 if (QFile(PATHTO_PMOUNT).exists() && QFile(PATHTO_PUMOUNT).exists()) 00135 MountCommand = QString("%1 %2") 00136 .arg((DoMount) ? PATHTO_PMOUNT : PATHTO_PUMOUNT) 00137 .arg(m_DevicePath); 00138 else 00139 MountCommand = QString("%1 %2") 00140 .arg((DoMount) ? PATHTO_MOUNT : PATHTO_UNMOUNT) 00141 .arg(m_DevicePath); 00142 00143 LOG(VB_MEDIA, LOG_INFO, QString("Executing '%1'").arg(MountCommand)); 00144 if (myth_system(MountCommand, kMSDontBlockInputDevs) == GENERIC_EXIT_OK) 00145 { 00146 if (DoMount) 00147 { 00148 // we cannot tell beforehand what the pmount mount point is 00149 // so verify the mount status of the device 00150 if (!findMountPath()) 00151 { 00152 LOG(VB_MEDIA, LOG_ERR, "performMountCmd() attempted to" 00153 " find mounted media, but failed?"); 00154 return false; 00155 } 00156 m_Status = MEDIASTAT_MOUNTED; 00157 onDeviceMounted(); 00158 LOG(VB_GENERAL, LOG_INFO, 00159 QString("Detected MediaType ") + MediaTypeString()); 00160 } 00161 else 00162 onDeviceUnmounted(); 00163 00164 return true; 00165 } 00166 else 00167 LOG(VB_GENERAL, LOG_ERR, QString("Failed to mount %1.") 00168 .arg(m_DevicePath)); 00169 } 00170 else 00171 { 00172 LOG(VB_MEDIA, LOG_INFO, "Disk inserted on a supermount device"); 00173 // If it's a super mount then the OS will handle mounting / unmounting. 00174 // We just need to give derived classes a chance to perform their 00175 // mount / unmount logic. 00176 if (DoMount) 00177 { 00178 onDeviceMounted(); 00179 LOG(VB_GENERAL, LOG_INFO, 00180 QString("Detected MediaType ") + MediaTypeString()); 00181 } 00182 else 00183 onDeviceUnmounted(); 00184 00185 return true; 00186 } 00187 return false; 00188 } 00189 00193 MythMediaType MythMediaDevice::DetectMediaType(void) 00194 { 00195 MythMediaType mediatype = MEDIATYPE_UNKNOWN; 00196 ext_cnt_t ext_cnt; 00197 00198 if (!ScanMediaType(m_MountPath, ext_cnt)) 00199 { 00200 LOG(VB_MEDIA, LOG_NOTICE, 00201 QString("No files with extensions found in '%1'") 00202 .arg(m_MountPath)); 00203 return mediatype; 00204 } 00205 00206 QMap<uint, uint> media_cnts, media_cnt; 00207 00208 // convert raw counts to composite mediatype counts 00209 ext_cnt_t::const_iterator it = ext_cnt.begin(); 00210 for (; it != ext_cnt.end(); ++it) 00211 { 00212 ext_to_media_t::const_iterator found = m_ext_to_media.find(it.key()); 00213 if (found != m_ext_to_media.end()) 00214 media_cnts[*found] += *it; 00215 } 00216 00217 // break composite mediatypes into constituent components 00218 QMap<uint, uint>::const_iterator cit = media_cnts.begin(); 00219 for (; cit != media_cnts.end(); ++cit) 00220 { 00221 for (uint key = 0, j = 0; key != MEDIATYPE_END; j++) 00222 { 00223 if ((key = 1 << j) & cit.key()) 00224 media_cnt[key] += *cit; 00225 } 00226 } 00227 00228 // decide on mediatype based on which one has a handler for > # of files 00229 uint max_cnt = 0; 00230 for (cit = media_cnt.begin(); cit != media_cnt.end(); ++cit) 00231 { 00232 if (*cit > max_cnt) 00233 { 00234 mediatype = (MythMediaType) cit.key(); 00235 max_cnt = *cit; 00236 } 00237 } 00238 00239 return mediatype; 00240 } 00241 00246 bool MythMediaDevice::ScanMediaType(const QString &directory, ext_cnt_t &cnt) 00247 { 00248 QDir d(directory); 00249 if (!d.exists()) 00250 return false; 00251 00252 00253 QFileInfoList list = d.entryInfoList(); 00254 00255 for( QFileInfoList::iterator it = list.begin(); 00256 it != list.end(); 00257 ++it ) 00258 { 00259 QFileInfo &fi = *it; 00260 00261 if (("." == fi.fileName()) || (".." == fi.fileName())) 00262 continue; 00263 00264 if (fi.isSymLink()) 00265 continue; 00266 00267 if (fi.isDir()) 00268 { 00269 ScanMediaType(fi.absoluteFilePath(), cnt); 00270 continue; 00271 } 00272 00273 const QString ext = fi.suffix(); 00274 if (!ext.isEmpty()) 00275 cnt[ext.toLower()]++; 00276 } 00277 00278 return !cnt.empty(); 00279 } 00280 00287 void MythMediaDevice::RegisterMediaExtensions(uint mediatype, 00288 const QString &extensions) 00289 { 00290 const QStringList list = extensions.split(","); 00291 for (QStringList::const_iterator it = list.begin(); it != list.end(); ++it) 00292 m_ext_to_media[*it] |= mediatype; 00293 } 00294 00295 MythMediaError MythMediaDevice::eject(bool open_close) 00296 { 00297 (void) open_close; 00298 00299 #if CONFIG_DARWIN 00300 // Backgrounding this is a bit naughty, but it can take up to five 00301 // seconds to execute, and freezing the frontend for that long is bad 00302 00303 QString command = "disktool -e " + m_DevicePath + " &"; 00304 00305 if (myth_system(command, kMSRunBackground) != GENERIC_EXIT_OK) 00306 return MEDIAERR_FAILED; 00307 00308 return MEDIAERR_OK; 00309 #endif 00310 00311 return MEDIAERR_UNSUPPORTED; 00312 } 00313 00314 bool MythMediaDevice::isSameDevice(const QString &path) 00315 { 00316 #ifdef Q_OS_MAC 00317 // The caller may be using a raw device instead of the BSD 'leaf' name 00318 if (path == "/dev/r" + m_DevicePath) 00319 return true; 00320 #endif 00321 00322 return (path == m_DevicePath); 00323 } 00324 00325 void MythMediaDevice::setSpeed(int speed) 00326 { 00327 LOG(VB_MEDIA, LOG_ERR, 00328 QString("Cannot setSpeed(%1) for device %2 - not implemented.") 00329 .arg(speed).arg(m_DevicePath)); 00330 } 00331 00332 MythMediaError MythMediaDevice::lock() 00333 { 00334 // We just open the device here, which may or may not do the trick, 00335 // derived classes can do more... 00336 if (openDevice()) 00337 { 00338 m_Locked = true; 00339 return MEDIAERR_OK; 00340 } 00341 m_Locked = false; 00342 return MEDIAERR_FAILED; 00343 } 00344 00345 MythMediaError MythMediaDevice::unlock() 00346 { 00347 m_Locked = false; 00348 00349 return MEDIAERR_OK; 00350 } 00351 00353 bool MythMediaDevice::isMounted(bool Verify) 00354 { 00355 if (Verify) 00356 return findMountPath(); 00357 else 00358 return (m_Status == MEDIASTAT_MOUNTED); 00359 } 00360 00362 bool MythMediaDevice::findMountPath() 00363 { 00364 if (m_DevicePath.isEmpty()) 00365 { 00366 LOG(VB_MEDIA, LOG_ERR, "findMountPath() - logic error, no device path"); 00367 return false; 00368 } 00369 00370 #ifdef USE_MOUNT_COMMAND 00371 // HACK. TODO: replace with something using popen()? 00372 if (myth_system(PATHTO_MOUNT + " > /tmp/mounts") != GENERIC_EXIT_OK) 00373 return false; 00374 QFile mountFile("/tmp/mounts"); 00375 #else 00376 QFile mountFile(PATHTO_MOUNTS); 00377 #endif 00378 00379 // Try to open the mounts file so we can search it for our device. 00380 if (!mountFile.open(QIODevice::ReadOnly)) 00381 return false; 00382 00383 QString debug; 00384 QTextStream stream(&mountFile); 00385 00386 for (;;) 00387 { 00388 QString mountPoint; 00389 QString deviceName; 00390 00391 00392 #ifdef USE_MOUNT_COMMAND 00393 // Extract mount point and device name from something like: 00394 // /dev/disk0s3 on / (hfs, local, journaled) - Mac OS X 00395 // /dev/hdd on /tmp/AAA BBB type udf (ro) - Linux 00396 stream >> deviceName; 00397 mountPoint = stream.readLine(); 00398 mountPoint.remove(" on "); 00399 mountPoint.remove(QRegExp(" type \\w.*")); // Linux 00400 mountPoint.remove(QRegExp(" \\(\\w.*")); // Mac OS X 00401 #else 00402 // Extract the mount point and device name. 00403 stream >> deviceName >> mountPoint; 00404 stream.readLine(); // skip the rest of the line 00405 #endif 00406 00407 if (deviceName.isNull()) 00408 break; 00409 00410 if (deviceName.isEmpty()) 00411 continue; 00412 00413 if (!deviceName.startsWith("/dev/")) 00414 continue; 00415 00416 QStringList deviceNames; 00417 getSymlinkTarget(deviceName, &deviceNames); 00418 00419 #if CONFIG_DARWIN 00420 // match short-style BSD node names: 00421 if (m_DevicePath.startsWith("disk")) 00422 deviceNames << deviceName.mid(5); // remove 5 chars - /dev/ 00423 #endif 00424 00425 // Deal with escaped spaces 00426 if (mountPoint.contains("\\040")) 00427 mountPoint.replace("\\040", " "); 00428 00429 00430 if (deviceNames.contains(m_DevicePath) || 00431 deviceNames.contains(m_RealDevice) ) 00432 { 00433 m_MountPath = mountPoint; 00434 mountFile.close(); 00435 return true; 00436 } 00437 00438 if (VERBOSE_LEVEL_CHECK(VB_MEDIA, LOG_DEBUG)) 00439 debug += QString(" %1 | %2\n") 00440 .arg(deviceName, 16).arg(mountPoint); 00441 } 00442 00443 mountFile.close(); 00444 00445 if (VERBOSE_LEVEL_CHECK(VB_MEDIA, LOG_DEBUG)) 00446 { 00447 debug = LOC + ":findMountPath() - mount of '" 00448 + m_DevicePath + "' not found.\n" 00449 + " Device name/type | Current mountpoint\n" 00450 + " -----------------+-------------------\n" 00451 + debug 00452 + " =================+==================="; 00453 LOG(VB_MEDIA, LOG_DEBUG, debug); 00454 } 00455 00456 return false; 00457 } 00458 00459 MythMediaStatus MythMediaDevice::setStatus( MythMediaStatus NewStatus, 00460 bool CloseIt ) 00461 { 00462 MythMediaStatus OldStatus = m_Status; 00463 00464 m_Status = NewStatus; 00465 00466 // If the status is changed we need to take some actions 00467 // depending on the old and new status. 00468 if (NewStatus != OldStatus) 00469 { 00470 switch (NewStatus) 00471 { 00472 // the disk is not / should not be mounted. 00473 case MEDIASTAT_ERROR: 00474 case MEDIASTAT_OPEN: 00475 case MEDIASTAT_NODISK: 00476 case MEDIASTAT_NOTMOUNTED: 00477 if (isMounted()) 00478 unmount(); 00479 break; 00480 case MEDIASTAT_UNKNOWN: 00481 case MEDIASTAT_USEABLE: 00482 case MEDIASTAT_MOUNTED: 00483 case MEDIASTAT_UNPLUGGED: 00484 // get rid of the compiler warning... 00485 break; 00486 } 00487 00488 // Don't fire off transitions to / from unknown states 00489 if (m_Status != MEDIASTAT_UNKNOWN && OldStatus != MEDIASTAT_UNKNOWN) 00490 emit statusChanged(OldStatus, this); 00491 } 00492 00493 00494 if (CloseIt) 00495 closeDevice(); 00496 00497 return m_Status; 00498 } 00499 00500 void MythMediaDevice::clearData() 00501 { 00502 m_VolumeID = QString::null; 00503 m_KeyID = QString::null; 00504 m_MediaType = MEDIATYPE_UNKNOWN; 00505 } 00506 00507 const char* MythMediaDevice::MediaTypeString() 00508 { 00509 return MediaTypeString(m_MediaType); 00510 } 00511 00512 const char* MythMediaDevice::MediaTypeString(MythMediaType type) 00513 { 00514 // MythMediaType is currently a bitmask. This code will only output the 00515 // first matched type. 00516 00517 if (type == MEDIATYPE_UNKNOWN) 00518 return "MEDIATYPE_UNKNOWN"; 00519 if (type & MEDIATYPE_DATA) 00520 return "MEDIATYPE_DATA"; 00521 if (type & MEDIATYPE_MIXED) 00522 return "MEDIATYPE_MIXED"; 00523 if (type & MEDIATYPE_AUDIO) 00524 return "MEDIATYPE_AUDIO"; 00525 if (type & MEDIATYPE_DVD) 00526 return "MEDIATYPE_DVD"; 00527 if (type & MEDIATYPE_BD) 00528 return "MEDIATYPE_BD"; 00529 if (type & MEDIATYPE_VCD) 00530 return "MEDIATYPE_VCD"; 00531 if (type & MEDIATYPE_MMUSIC) 00532 return "MEDIATYPE_MMUSIC"; 00533 if (type & MEDIATYPE_MVIDEO) 00534 return "MEDIATYPE_MVIDEO"; 00535 if (type & MEDIATYPE_MGALLERY) 00536 return "MEDIATYPE_MGALLERY"; 00537 00538 return "MEDIATYPE_UNKNOWN"; 00539 } 00540
1.7.6.1