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 <qfile.h>
00010 #include <qdir.h>
00011
00012
00013 #include "mythmedia.h"
00014 #include "mythconfig.h"
00015 #include "mythcontext.h"
00016 #include "util.h"
00017
00018 using namespace std;
00019
00020 #ifdef USING_MINGW
00021 # define O_NONBLOCK 0
00022 #endif
00023
00024 #define LOC QString("MythMediaDevice:")
00025 #define LOC_WARN QString("MythMediaDevice, Warning: ")
00026 #define LOC_ERR QString("MythMediaDevice, Error: ")
00027
00028 static const QString PATHTO_PMOUNT("/usr/bin/pmount");
00029 static const QString PATHTO_PUMOUNT("/usr/bin/pumount");
00030 static const QString PATHTO_MOUNT("/bin/mount");
00031 static const QString PATHTO_UNMOUNT("/bin/umount");
00032 static const QString PATHTO_MOUNTS("/proc/mounts");
00033
00034 const char* MythMediaDevice::MediaStatusStrings[] =
00035 {
00036 "MEDIASTAT_ERROR",
00037 "MEDIASTAT_UNKNOWN",
00038 "MEDIASTAT_UNPLUGGED",
00039 "MEDIASTAT_OPEN",
00040 "MEDIASTAT_NODISK",
00041 "MEDIASTAT_UNFORMATTED",
00042 "MEDIASTAT_USEABLE",
00043 "MEDIASTAT_NOTMOUNTED",
00044 "MEDIASTAT_MOUNTED"
00045 };
00046
00047 const char* MythMediaDevice::MediaErrorStrings[] =
00048 {
00049 "MEDIAERR_OK",
00050 "MEDIAERR_FAILED",
00051 "MEDIAERR_UNSUPPORTED"
00052 };
00053
00054 MythMediaDevice::MythMediaDevice(QObject* par, const char* DevicePath,
00055 bool SuperMount, bool AllowEject)
00056 : QObject(par)
00057 {
00058 m_DevicePath = DevicePath;
00059 m_AllowEject = AllowEject;
00060 m_Locked = false;
00061 m_DeviceHandle = -1;
00062 m_SuperMount = SuperMount;
00063 m_Status = MEDIASTAT_UNKNOWN;
00064 m_MediaType = MEDIATYPE_UNKNOWN;
00065
00066 QFileInfo fi(DevicePath);
00067 if (fi.isSymLink())
00068 m_RealDevice = m_DevicePath.section('/', 0, -2) + "/" + fi.readLink();
00069 else
00070 m_RealDevice = QString::null;
00071 }
00072
00073 bool MythMediaDevice::openDevice()
00074 {
00075
00076 if (isDeviceOpen())
00077 return true;
00078
00079 m_DeviceHandle = open(m_DevicePath, O_RDONLY | O_NONBLOCK);
00080
00081 return isDeviceOpen();
00082 }
00083
00084 bool MythMediaDevice::closeDevice()
00085 {
00086
00087 if (!isDeviceOpen())
00088 return true;
00089
00090 int ret = close(m_DeviceHandle);
00091 m_DeviceHandle = -1;
00092
00093 return (ret != -1) ? true : false;
00094 }
00095
00096 bool MythMediaDevice::isDeviceOpen() const
00097 {
00098 return (m_DeviceHandle >= 0) ? true : false;
00099 }
00100
00101 bool MythMediaDevice::performMountCmd(bool DoMount)
00102 {
00103 if (DoMount && isMounted(true))
00104 {
00105 VERBOSE(VB_MEDIA, "MythMediaDevice::performMountCmd(true)"
00106 " - Logic Error? Device already mounted.");
00107 return true;
00108 }
00109
00110 if (isDeviceOpen())
00111 closeDevice();
00112
00113 if (!m_SuperMount)
00114 {
00115 QString MountCommand;
00116
00117
00118
00119 if (QFile(PATHTO_PMOUNT).exists() && QFile(PATHTO_PUMOUNT).exists())
00120 MountCommand = QString("%1 %2")
00121 .arg((DoMount) ? PATHTO_PMOUNT : PATHTO_PUMOUNT)
00122 .arg(m_DevicePath);
00123 else
00124 MountCommand = QString("%1 %2")
00125 .arg((DoMount) ? PATHTO_MOUNT : PATHTO_UNMOUNT)
00126 .arg(m_DevicePath);
00127
00128 VERBOSE(VB_MEDIA, QString("Executing '%1'").arg(MountCommand));
00129 if (0 == myth_system(MountCommand))
00130 {
00131 if (DoMount)
00132 {
00133
00134
00135 isMounted(true);
00136 m_Status = MEDIASTAT_MOUNTED;
00137 onDeviceMounted();
00138 VERBOSE(VB_GENERAL,
00139 QString("Detected MediaType ") + MediaTypeString());
00140 }
00141 else
00142 onDeviceUnmounted();
00143 return true;
00144 }
00145 else
00146 {
00147 VERBOSE(VB_GENERAL, QString("Failed to mount %1.")
00148 .arg(m_DevicePath));
00149 }
00150 }
00151 else
00152 {
00153 VERBOSE(VB_MEDIA, "Disk inserted on a supermount device");
00154
00155
00156
00157 if (DoMount)
00158 {
00159 onDeviceMounted();
00160 VERBOSE(VB_GENERAL,
00161 QString("Detected MediaType ") + MediaTypeString());
00162 }
00163 else
00164 onDeviceUnmounted();
00165 return true;
00166 }
00167 return false;
00168 }
00169
00173 MediaType MythMediaDevice::DetectMediaType(void)
00174 {
00175 MediaType mediatype = MEDIATYPE_UNKNOWN;
00176 ext_cnt_t ext_cnt;
00177
00178 if (!ScanMediaType(m_MountPath, ext_cnt))
00179 {
00180 VERBOSE(VB_MEDIA, QString("No files with extensions found in '%1'")
00181 .arg(m_MountPath));
00182 return mediatype;
00183 }
00184
00185 QMap<uint, uint> media_cnts, media_cnt;
00186
00187
00188 ext_cnt_t::const_iterator it = ext_cnt.begin();
00189 for (; it != ext_cnt.end(); ++it)
00190 {
00191 ext_to_media_t::const_iterator found = m_ext_to_media.find(it.key());
00192 if (found != m_ext_to_media.end())
00193 media_cnts[*found] += *it;
00194 }
00195
00196
00197 QMap<uint, uint>::const_iterator cit = media_cnts.begin();
00198 for (; cit != media_cnts.end(); ++cit)
00199 {
00200 for (uint key = 0, j = 0; key != MEDIATYPE_END; j++)
00201 {
00202 if ((key = 1 << j) & cit.key())
00203 media_cnt[key] += *cit;
00204 }
00205 }
00206
00207
00208 uint max_cnt = 0;
00209 for (cit = media_cnt.begin(); cit != media_cnt.end(); ++cit)
00210 {
00211 if (*cit > max_cnt)
00212 {
00213 mediatype = (MediaType) cit.key();
00214 max_cnt = *cit;
00215 }
00216 }
00217
00218 return mediatype;
00219 }
00220
00225 bool MythMediaDevice::ScanMediaType(const QString &directory, ext_cnt_t &cnt)
00226 {
00227 QDir d(directory);
00228 if (!d.exists())
00229 return false;
00230
00231 const QFileInfoList *list = d.entryInfoList();
00232 if (!list)
00233 return false;
00234
00235 QFileInfoListIterator it(*list);
00236
00237 for (; it.current(); ++it)
00238 {
00239 if (("." == (*it)->fileName()) || (".." == (*it)->fileName()))
00240 continue;
00241
00242 if ((*it)->isDir())
00243 {
00244 ScanMediaType((*it)->absFilePath(), cnt);
00245 continue;
00246 }
00247
00248 const QString ext = (*it)->extension(false);
00249 if (!ext.isEmpty())
00250 cnt[ext.lower()]++;
00251 }
00252
00253 return !cnt.empty();
00254 }
00255
00262 void MythMediaDevice::RegisterMediaExtensions(uint mediatype,
00263 const QString &extensions)
00264 {
00265 const QStringList list = QStringList::split(",", extensions, "");
00266 for (QStringList::const_iterator it = list.begin(); it != list.end(); ++it)
00267 m_ext_to_media[*it] |= mediatype;
00268 }
00269
00270 MediaError MythMediaDevice::eject(bool open_close)
00271 {
00272 (void) open_close;
00273
00274 #ifdef CONFIG_DARWIN
00275
00276
00277
00278 QString command = "disktool -e " + m_DevicePath + " &";
00279
00280 if (myth_system(command) > 0)
00281 return MEDIAERR_FAILED;
00282
00283 return MEDIAERR_OK;
00284 #endif
00285
00286 return MEDIAERR_UNSUPPORTED;
00287 }
00288
00289 bool MythMediaDevice::isSameDevice(const QString &path)
00290 {
00291 #ifdef Q_OS_MAC
00292
00293 if (path == "/dev/r" + m_DevicePath)
00294 return true;
00295 #endif
00296
00297 return (path == m_DevicePath);
00298 }
00299
00300 void MythMediaDevice::setSpeed(int speed)
00301 {
00302 VERBOSE(VB_MEDIA,
00303 QString("Cannot setSpeed(%1) for device %2 - not implemented.")
00304 .arg(speed).arg(m_DevicePath));
00305 }
00306
00307 MediaError MythMediaDevice::lock()
00308 {
00309
00310
00311 if (openDevice())
00312 {
00313 m_Locked = true;
00314 return MEDIAERR_OK;
00315 }
00316 m_Locked = false;
00317 return MEDIAERR_FAILED;
00318 }
00319
00320 MediaError MythMediaDevice::unlock()
00321 {
00322 m_Locked = false;
00323
00324 return MEDIAERR_OK;
00325 }
00326
00328 bool MythMediaDevice::isMounted(bool Verify)
00329 {
00330 if (!Verify)
00331 return (m_Status == MEDIASTAT_MOUNTED);
00332
00333 if (m_DevicePath.isEmpty())
00334 {
00335 VERBOSE(VB_MEDIA, LOC + ":isMounted() - logic error, no device path");
00336 return false;
00337 }
00338
00339 QFile mountFile(PATHTO_MOUNTS);
00340
00341
00342 if (!mountFile.open(IO_ReadOnly))
00343 return false;
00344
00345 QString debug;
00346 QTextStream stream(&mountFile);
00347
00348 while (!stream.eof())
00349 {
00350 QString mountPoint;
00351 QString deviceName;
00352
00353
00354
00355 stream >> deviceName >> mountPoint;
00356 stream.readLine();
00357
00358 if (deviceName.isEmpty())
00359 continue;
00360
00361 if (!deviceName.startsWith("/dev/"))
00362 continue;
00363
00364 QStringList deviceNames = deviceName;
00365
00366
00367
00368 QFileInfo fi(deviceName);
00369 QString link = QString::null;
00370
00371
00372 if (fi.isSymLink() && !(link = fi.readLink()).isEmpty())
00373 {
00374 if (link[0] == '/')
00375 deviceNames.push_back(link);
00376 else
00377 deviceNames.push_back(fi.dir(true).absPath() + "/" + link);
00378 }
00379
00380
00381
00382 if (mountPoint.contains("\\040"))
00383 mountPoint.replace("\\040", " ");
00384
00385
00386 if (deviceNames.contains(m_DevicePath) ||
00387 deviceNames.contains(m_RealDevice) )
00388 {
00389 m_MountPath = mountPoint;
00390 mountFile.close();
00391 return true;
00392 }
00393
00394 if (print_verbose_messages & VB_MEDIA)
00395 debug += QString(" %1 | %2\n")
00396 .arg(deviceName, 16).arg(mountPoint);
00397 }
00398
00399 mountFile.close();
00400
00401 if (print_verbose_messages & VB_MEDIA)
00402 {
00403 debug = LOC + ":isMounted() - mount of '"
00404 + m_DevicePath + "' not found.\n"
00405 + " Device name/type | Current mountpoint\n"
00406 + " -----------------+-------------------\n"
00407 + debug
00408 + " =================+===================";
00409 VERBOSE(VB_MEDIA, debug);
00410 }
00411
00412 return false;
00413 }
00414
00415 MediaStatus MythMediaDevice::setStatus( MediaStatus NewStatus, bool CloseIt )
00416 {
00417 MediaStatus OldStatus = m_Status;
00418
00419 m_Status = NewStatus;
00420
00421
00422
00423 if (NewStatus != OldStatus)
00424 {
00425 switch (NewStatus)
00426 {
00427
00428 case MEDIASTAT_ERROR:
00429 case MEDIASTAT_OPEN:
00430 case MEDIASTAT_NODISK:
00431 case MEDIASTAT_NOTMOUNTED:
00432 if (isMounted(true))
00433 unmount();
00434 break;
00435 case MEDIASTAT_UNKNOWN:
00436 case MEDIASTAT_USEABLE:
00437 case MEDIASTAT_MOUNTED:
00438 case MEDIASTAT_UNPLUGGED:
00439
00440 break;
00441 }
00442
00443
00444 if (m_Status != MEDIASTAT_UNKNOWN && OldStatus != MEDIASTAT_UNKNOWN)
00445 emit statusChanged(OldStatus, this);
00446 }
00447
00448
00449 if (CloseIt)
00450 closeDevice();
00451
00452 return m_Status;
00453 }
00454
00455 void MythMediaDevice::clearData()
00456 {
00457 m_VolumeID = QString::null;
00458 m_KeyID = QString::null;
00459 m_MediaType = MEDIATYPE_UNKNOWN;
00460 }
00461
00462 const char* MythMediaDevice::MediaTypeString()
00463 {
00464 return MediaTypeString(m_MediaType);
00465 }
00466
00467 const char* MythMediaDevice::MediaTypeString(MediaType type)
00468 {
00469
00470
00471
00472 if (type == MEDIATYPE_UNKNOWN)
00473 return "MEDIATYPE_UNKNOWN";
00474 if (type & MEDIATYPE_DATA)
00475 return "MEDIATYPE_DATA";
00476 if (type & MEDIATYPE_MIXED)
00477 return "MEDIATYPE_MIXED";
00478 if (type & MEDIATYPE_AUDIO)
00479 return "MEDIATYPE_AUDIO";
00480 if (type & MEDIATYPE_DVD)
00481 return "MEDIATYPE_DVD";
00482 if (type & MEDIATYPE_VCD)
00483 return "MEDIATYPE_VCD";
00484 if (type & MEDIATYPE_MMUSIC)
00485 return "MEDIATYPE_MMUSIC";
00486 if (type & MEDIATYPE_MVIDEO)
00487 return "MEDIATYPE_MVIDEO";
00488 if (type & MEDIATYPE_MGALLERY)
00489 return "MEDIATYPE_MGALLERY";
00490
00491 return "MEDIATYPE_UNKNOWN";
00492 };
00493