00001
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
00009 #include <QDir>
00010 #include <QFileInfo>
00011 #include <QFileInfoList>
00012 #include <QTextStream>
00013
00014
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
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
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
00116
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
00133
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
00149
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
00174
00175
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
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
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
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
00301
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
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
00335
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
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
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
00394
00395
00396 stream >> deviceName;
00397 mountPoint = stream.readLine();
00398 mountPoint.remove(" on ");
00399 mountPoint.remove(QRegExp(" type \\w.*"));
00400 mountPoint.remove(QRegExp(" \\(\\w.*"));
00401 #else
00402
00403 stream >> deviceName >> mountPoint;
00404 stream.readLine();
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
00421 if (m_DevicePath.startsWith("disk"))
00422 deviceNames << deviceName.mid(5);
00423 #endif
00424
00425
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
00467
00468 if (NewStatus != OldStatus)
00469 {
00470 switch (NewStatus)
00471 {
00472
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
00485 break;
00486 }
00487
00488
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
00515
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