MythTV  0.28pre
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Groups Pages
mythmedia.cpp
Go to the documentation of this file.
1 // C header
2 #include <fcntl.h>
3 #include <unistd.h>
4 #include <sys/types.h>
5 #include <sys/stat.h>
6 #include <sys/param.h>
7 
8 // Qt Headers
9 #include <QDir>
10 #include <QFileInfo>
11 #include <QFileInfoList>
12 #include <QTextStream>
13 
14 // MythTV headers
15 #include "mythmedia.h"
16 #include "mythconfig.h"
17 #include "mythlogging.h"
18 #include "mythmiscutil.h"
19 #include "mythsystemlegacy.h"
20 #include "exitcodes.h"
21 
22 using namespace std;
23 
24 #ifdef _WIN32
25 # define O_NONBLOCK 0
26 #endif
27 
28 #define LOC QString("MythMediaDevice:")
29 
30 static const QString PATHTO_PMOUNT("/usr/bin/pmount");
31 static const QString PATHTO_PUMOUNT("/usr/bin/pumount");
32 #if CONFIG_DARWIN
33  static const QString PATHTO_MOUNT("/sbin/mount");
34 #else
35  static const QString PATHTO_MOUNT("/bin/mount");
36 #endif
37 static const QString PATHTO_UNMOUNT("/bin/umount");
38 static const QString PATHTO_MOUNTS("/proc/mounts");
39 
40 #if CONFIG_DARWIN
41 # define USE_MOUNT_COMMAND
42 #endif
43 
45 {
46  "MEDIASTAT_ERROR",
47  "MEDIASTAT_UNKNOWN",
48  "MEDIASTAT_UNPLUGGED",
49  "MEDIASTAT_OPEN",
50  "MEDIASTAT_NODISK",
51  "MEDIASTAT_UNFORMATTED",
52  "MEDIASTAT_USEABLE",
53  "MEDIASTAT_NOTMOUNTED",
54  "MEDIASTAT_MOUNTED"
55 };
56 
58 {
59  "MEDIAERR_OK",
60  "MEDIAERR_FAILED",
61  "MEDIAERR_UNSUPPORTED"
62 };
63 
64 QEvent::Type MythMediaEvent::kEventType =
65  (QEvent::Type) QEvent::registerEventType();
66 
67 MythMediaDevice::MythMediaDevice(QObject* par, const char* DevicePath,
68  bool SuperMount, bool AllowEject)
69  : QObject(par)
70 {
71  m_DevicePath = DevicePath;
72  m_AllowEject = AllowEject;
73  m_Locked = false;
74  m_DeviceHandle = -1;
75  m_SuperMount = SuperMount;
79 }
80 
82 {
83  // Sanity check
84  if (isDeviceOpen())
85  return true;
86 
87  QByteArray dev = m_DevicePath.toLocal8Bit();
88  m_DeviceHandle = open(dev.constData(), O_RDONLY | O_NONBLOCK);
89 
90  return isDeviceOpen();
91 }
92 
94 {
95  // Sanity check
96  if (!isDeviceOpen())
97  return true;
98 
99  int ret = close(m_DeviceHandle);
100  m_DeviceHandle = -1;
101 
102  return (ret != -1) ? true : false;
103 }
104 
106 {
107  return (m_DeviceHandle >= 0) ? true : false;
108 }
109 
111 {
112  if (DoMount && isMounted())
113  {
114 #ifdef Q_OS_MAC
115  // Not an error - DiskArbitration has already mounted the device.
116  // AddDevice calls mount() so onDeviceMounted() can get mediaType.
117  onDeviceMounted();
118 #else
119  LOG(VB_MEDIA, LOG_ERR, "MythMediaDevice::performMountCmd(true)"
120  " - Logic Error? Device already mounted.");
121  return true;
122 #endif
123  }
124 
125  if (isDeviceOpen())
126  closeDevice();
127 
128  if (!m_SuperMount)
129  {
130  QString MountCommand;
131 
132  // Build a command line for mount/unmount and execute it...
133  // Is there a better way to do this?
134  if (QFile(PATHTO_PMOUNT).exists() && QFile(PATHTO_PUMOUNT).exists())
135  MountCommand = QString("%1 %2")
136  .arg((DoMount) ? PATHTO_PMOUNT : PATHTO_PUMOUNT)
137  .arg(m_DevicePath);
138  else
139  MountCommand = QString("%1 %2")
140  .arg((DoMount) ? PATHTO_MOUNT : PATHTO_UNMOUNT)
141  .arg(m_DevicePath);
142 
143  LOG(VB_MEDIA, LOG_INFO, QString("Executing '%1'").arg(MountCommand));
144  if (myth_system(MountCommand, kMSDontBlockInputDevs) == GENERIC_EXIT_OK)
145  {
146  if (DoMount)
147  {
148  // we cannot tell beforehand what the pmount mount point is
149  // so verify the mount status of the device
150  if (!findMountPath())
151  {
152  LOG(VB_MEDIA, LOG_ERR, "performMountCmd() attempted to"
153  " find mounted media, but failed?");
154  return false;
155  }
157  onDeviceMounted();
158  LOG(VB_GENERAL, LOG_INFO,
159  QString("Detected MediaType ") + MediaTypeString());
160  }
161  else
163 
164  return true;
165  }
166  else
167  LOG(VB_GENERAL, LOG_ERR, QString("Failed to mount %1.")
168  .arg(m_DevicePath));
169  }
170  else
171  {
172  LOG(VB_MEDIA, LOG_INFO, "Disk inserted on a supermount device");
173  // If it's a super mount then the OS will handle mounting / unmounting.
174  // We just need to give derived classes a chance to perform their
175  // mount / unmount logic.
176  if (DoMount)
177  {
178  onDeviceMounted();
179  LOG(VB_GENERAL, LOG_INFO,
180  QString("Detected MediaType ") + MediaTypeString());
181  }
182  else
184 
185  return true;
186  }
187  return false;
188 }
189 
194 {
196  ext_cnt_t ext_cnt;
197 
198  if (!ScanMediaType(m_MountPath, ext_cnt))
199  {
200  LOG(VB_MEDIA, LOG_NOTICE,
201  QString("No files with extensions found in '%1'")
202  .arg(m_MountPath));
203  return mediatype;
204  }
205 
206  QMap<uint, uint> media_cnts, media_cnt;
207 
208  // convert raw counts to composite mediatype counts
209  ext_cnt_t::const_iterator it = ext_cnt.begin();
210  for (; it != ext_cnt.end(); ++it)
211  {
212  ext_to_media_t::const_iterator found = m_ext_to_media.find(it.key());
213  if (found != m_ext_to_media.end())
214  media_cnts[*found] += *it;
215  }
216 
217  // break composite mediatypes into constituent components
218  QMap<uint, uint>::const_iterator cit = media_cnts.begin();
219  for (; cit != media_cnts.end(); ++cit)
220  {
221  for (uint key = 0, j = 0; key != MEDIATYPE_END; j++)
222  {
223  if ((key = 1 << j) & cit.key())
224  media_cnt[key] += *cit;
225  }
226  }
227 
228  // decide on mediatype based on which one has a handler for > # of files
229  uint max_cnt = 0;
230  for (cit = media_cnt.begin(); cit != media_cnt.end(); ++cit)
231  {
232  if (*cit > max_cnt)
233  {
234  mediatype = (MythMediaType) cit.key();
235  max_cnt = *cit;
236  }
237  }
238 
239  return mediatype;
240 }
241 
246 bool MythMediaDevice::ScanMediaType(const QString &directory, ext_cnt_t &cnt)
247 {
248  QDir d(directory);
249  if (!d.exists())
250  return false;
251 
252  d.setFilter(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot);
253  QFileInfoList list = d.entryInfoList();
254 
255  for( QFileInfoList::iterator it = list.begin();
256  it != list.end();
257  ++it )
258  {
259  QFileInfo &fi = *it;
260 
261  if (fi.isSymLink())
262  continue;
263 
264  if (fi.isDir())
265  {
266  ScanMediaType(fi.absoluteFilePath(), cnt);
267  continue;
268  }
269 
270  const QString ext = fi.suffix();
271  if (!ext.isEmpty())
272  cnt[ext.toLower()]++;
273  }
274 
275  return !cnt.empty();
276 }
277 
285  const QString &extensions)
286 {
287  const QStringList list = extensions.split(",");
288  for (QStringList::const_iterator it = list.begin(); it != list.end(); ++it)
289  m_ext_to_media[*it] |= mediatype;
290 }
291 
293 {
294  (void) open_close;
295 
296 #if CONFIG_DARWIN
297  QString command = "diskutil eject " + m_DevicePath;
298 
299  int ret = myth_system(command, kMSRunBackground);
300  return MEDIAERR_OK;
301 #endif
302 
303  return MEDIAERR_UNSUPPORTED;
304 }
305 
306 bool MythMediaDevice::isSameDevice(const QString &path)
307 {
308 #ifdef Q_OS_MAC
309  // The caller may be using a raw device instead of the BSD 'leaf' name
310  if (path == "/dev/r" + m_DevicePath)
311  return true;
312 #endif
313 
314  return (path == m_DevicePath);
315 }
316 
318 {
319  setDeviceSpeed(m_DevicePath.toLocal8Bit().constData(), speed);
320 }
321 
323 {
324  // We just open the device here, which may or may not do the trick,
325  // derived classes can do more...
326  if (openDevice())
327  {
328  m_Locked = true;
329  return MEDIAERR_OK;
330  }
331  m_Locked = false;
332  return MEDIAERR_FAILED;
333 }
334 
336 {
337  m_Locked = false;
338 
339  return MEDIAERR_OK;
340 }
341 
343 bool MythMediaDevice::isMounted(bool Verify)
344 {
345  if (Verify)
346  return findMountPath();
347  else
348  return (m_Status == MEDIASTAT_MOUNTED);
349 }
350 
353 {
354  if (m_DevicePath.isEmpty())
355  {
356  LOG(VB_MEDIA, LOG_ERR, "findMountPath() - logic error, no device path");
357  return false;
358  }
359 
360 #ifdef USE_MOUNT_COMMAND
361  // HACK. TODO: replace with something using popen()?
362  if (myth_system(PATHTO_MOUNT + " > /tmp/mounts") != GENERIC_EXIT_OK)
363  return false;
364  QFile mountFile("/tmp/mounts");
365 #else
366  QFile mountFile(PATHTO_MOUNTS);
367 #endif
368 
369  // Try to open the mounts file so we can search it for our device.
370  if (!mountFile.open(QIODevice::ReadOnly))
371  return false;
372 
373  QString debug;
374  QTextStream stream(&mountFile);
375 
376  for (;;)
377  {
378  QString mountPoint;
379  QString deviceName;
380 
381 
382 #ifdef USE_MOUNT_COMMAND
383  // Extract mount point and device name from something like:
384  // /dev/disk0s3 on / (hfs, local, journaled) - Mac OS X
385  // /dev/hdd on /tmp/AAA BBB type udf (ro) - Linux
386  stream >> deviceName;
387  mountPoint = stream.readLine();
388  mountPoint.remove(" on ");
389  mountPoint.remove(QRegExp(" type \\w.*")); // Linux
390  mountPoint.remove(QRegExp(" \\(\\w.*")); // Mac OS X
391 #else
392  // Extract the mount point and device name.
393  stream >> deviceName >> mountPoint;
394  stream.readLine(); // skip the rest of the line
395 #endif
396 
397  if (deviceName.isNull())
398  break;
399 
400  if (deviceName.isEmpty())
401  continue;
402 
403  if (!deviceName.startsWith("/dev/"))
404  continue;
405 
406  QStringList deviceNames;
407  getSymlinkTarget(deviceName, &deviceNames);
408 
409 #if CONFIG_DARWIN
410  // match short-style BSD node names:
411  if (m_DevicePath.startsWith("disk"))
412  deviceNames << deviceName.mid(5); // remove 5 chars - /dev/
413 #endif
414 
415  // Deal with escaped spaces
416  if (mountPoint.contains("\\040"))
417  mountPoint.replace("\\040", " ");
418 
419 
420  if (deviceNames.contains(m_DevicePath) ||
421  deviceNames.contains(m_RealDevice) )
422  {
423  m_MountPath = mountPoint;
424  mountFile.close();
425  return true;
426  }
427 
428  if (VERBOSE_LEVEL_CHECK(VB_MEDIA, LOG_DEBUG))
429  debug += QString(" %1 | %2\n")
430  .arg(deviceName, 16).arg(mountPoint);
431  }
432 
433  mountFile.close();
434 
435  if (VERBOSE_LEVEL_CHECK(VB_MEDIA, LOG_DEBUG))
436  {
437  debug = LOC + ":findMountPath() - mount of '"
438  + m_DevicePath + "' not found.\n"
439  + " Device name/type | Current mountpoint\n"
440  + " -----------------+-------------------\n"
441  + debug
442  + " =================+===================";
443  LOG(VB_MEDIA, LOG_DEBUG, debug);
444  }
445 
446  return false;
447 }
448 
450  bool CloseIt )
451 {
452  MythMediaStatus OldStatus = m_Status;
453 
454  m_Status = NewStatus;
455 
456  // If the status is changed we need to take some actions
457  // depending on the old and new status.
458  if (NewStatus != OldStatus)
459  {
460  switch (NewStatus)
461  {
462  // the disk is not / should not be mounted.
463  case MEDIASTAT_ERROR:
464  case MEDIASTAT_OPEN:
465  case MEDIASTAT_NODISK:
467  if (isMounted())
468  unmount();
469  break;
470  case MEDIASTAT_UNKNOWN:
471  case MEDIASTAT_USEABLE:
472  case MEDIASTAT_MOUNTED:
473  case MEDIASTAT_UNPLUGGED:
475  // get rid of the compiler warning...
476  break;
477  }
478 
479  // Don't fire off transitions to / from unknown states
480  if (m_Status != MEDIASTAT_UNKNOWN && OldStatus != MEDIASTAT_UNKNOWN)
481  emit statusChanged(OldStatus, this);
482  }
483 
484 
485  if (CloseIt)
486  closeDevice();
487 
488  return m_Status;
489 }
490 
492 {
493  m_VolumeID = QString::null;
494  m_KeyID = QString::null;
496 }
497 
499 {
501 }
502 
504 {
505  // MythMediaType is currently a bitmask. This code will only output the
506  // first matched type.
507 
508  if (type == MEDIATYPE_UNKNOWN)
509  return "MEDIATYPE_UNKNOWN";
510  if (type & MEDIATYPE_DATA)
511  return "MEDIATYPE_DATA";
512  if (type & MEDIATYPE_MIXED)
513  return "MEDIATYPE_MIXED";
514  if (type & MEDIATYPE_AUDIO)
515  return "MEDIATYPE_AUDIO";
516  if (type & MEDIATYPE_DVD)
517  return "MEDIATYPE_DVD";
518  if (type & MEDIATYPE_BD)
519  return "MEDIATYPE_BD";
520  if (type & MEDIATYPE_VCD)
521  return "MEDIATYPE_VCD";
522  if (type & MEDIATYPE_MMUSIC)
523  return "MEDIATYPE_MMUSIC";
524  if (type & MEDIATYPE_MVIDEO)
525  return "MEDIATYPE_MVIDEO";
526  if (type & MEDIATYPE_MGALLERY)
527  return "MEDIATYPE_MGALLERY";
528 
529  return "MEDIATYPE_UNKNOWN";
530 }
531 
virtual bool performMountCmd(bool DoMount)
Definition: mythmedia.cpp:110
static const QString PATHTO_PMOUNT("/usr/bin/pmount")
open(CONF, $file) or die("\nERROR
CD/DVD tray open (meaningless for non-CDs?)
Definition: mythmedia.h:16
MythMediaType
Definition: mythmedia.h:24
typedef void(__LZO_CDECL *lzo_free_func_t)(lzo_callback_p self
bool m_AllowEject
Allow the user to eject the media?. Read only.
Definition: mythmedia.h:163
virtual bool closeDevice()
Definition: mythmedia.cpp:93
QString m_VolumeID
The volume ID of the media. Read/write.
Definition: mythmedia.h:157
MythMediaStatus m_Status
The status of the media as of the.
Definition: mythmedia.h:159
virtual void setDeviceSpeed(const char *, int)
Definition: mythmedia.h:100
bool m_Locked
Is this media locked?. Read only.
Definition: mythmedia.h:164
Unable to mount, but could be usable.
Definition: mythmedia.h:13
run child in the background
Definition: mythsystem.h:39
virtual bool openDevice()
Definition: mythmedia.cpp:81
void RegisterMediaExtensions(uint mediatype, const QString &extensions)
Used to register media types with extensions.
Definition: mythmedia.cpp:284
QString m_DevicePath
The path to this media's device.
Definition: mythmedia.h:149
QString getSymlinkTarget(const QString &start_file, QStringList *intermediaries, unsigned maxLinks)
unsigned int uint
Definition: compat.h:135
GLint GLenum GLsizei GLint GLenum GLenum type
bool m_SuperMount
Is this a supermount device?. Read only.
Definition: mythmedia.h:165
bool ScanMediaType(const QString &directory, ext_cnt_t &counts)
Recursively scan directories and create an associative array with the number of times we've seen each...
Definition: mythmedia.cpp:246
MythMediaStatus setStatus(MythMediaStatus newStat, bool CloseIt=false)
Definition: mythmedia.cpp:449
QMap< QString, uint > ext_cnt_t
Definition: mythmedia.h:45
static const QString PATHTO_MOUNTS("/proc/mounts")
const char * MediaTypeString()
Definition: mythmedia.cpp:498
static const char * MediaStatusStrings[]
Definition: mythmedia.h:117
static const char * MediaErrorStrings[]
Definition: mythmedia.h:118
QString m_KeyID
KeyID of the media.
Definition: mythmedia.h:151
virtual MythMediaError unlock()
Definition: mythmedia.cpp:335
BOOL FAR PASCAL short short long d
Definition: minilzo.cpp:2526
MythMediaError
Definition: mythmedia.h:39
virtual MythMediaError lock()
Definition: mythmedia.cpp:322
For devices/media a plugin might erase/format.
Definition: mythmedia.h:18
QString m_RealDevice
If m_DevicePath is a symlink, its target.
Definition: mythmedia.h:155
VERBOSE_PREAMBLE Most debug(nodatabase, notimestamp, noextra)") VERBOSE_MAP(VB_IMPORTANT
MythMediaType DetectMediaType(void)
Returns guessed media type based on file extensions.
Definition: mythmedia.cpp:193
uint myth_system(const QString &command, uint flags, uint timeout)
int m_DeviceHandle
A file handle for opening and closing the device, ioctls(), et c.
Definition: mythmedia.h:167
ext_to_media_t m_ext_to_media
Map of extension to media type.
Definition: mythmedia.h:172
virtual void setSpeed(int speed)
Definition: mythmedia.cpp:317
virtual void onDeviceUnmounted()
Override this to perform any post unmount logic.
Definition: mythmedia.h:141
static const QString PATHTO_UNMOUNT("/bin/umount")
virtual MythMediaError eject(bool open_close=true)
Definition: mythmedia.cpp:292
static Type kEventType
Definition: mythmedia.h:184
voidpf stream
Definition: ioapi.h:136
bool findMountPath()
Try to find a mount of m_DevicePath in the mounts file.
Definition: mythmedia.cpp:352
bool isDeviceOpen() const
Definition: mythmedia.cpp:105
virtual void onDeviceMounted(void)
Override this to perform any post mount logic.
Definition: mythmedia.h:133
MythMediaType m_MediaType
last call to checkMedia. Read only
Definition: mythmedia.h:161
static const QString PATHTO_MOUNT("/sbin/mount")
bool isMounted(bool bVerify=true)
Tells us if m_DevicePath is a mounted device.
Definition: mythmedia.cpp:343
CD/DVD tray closed but empty, device unusable.
Definition: mythmedia.h:17
static const QString LOC
MythMediaStatus
Definition: mythmedia.h:12
QString m_MountPath
The path to this media's mount point.
Definition: mythmedia.h:153
close(CACHE)
void statusChanged(MythMediaStatus oldStatus, MythMediaDevice *pMedia)
MythMediaDevice(QObject *par, const char *DevicePath, bool SuperMount, bool AllowEject)
Definition: mythmedia.cpp:67
bool unmount()
Definition: mythmedia.h:108
virtual bool isSameDevice(const QString &path)
Definition: mythmedia.cpp:306
static const QString PATHTO_PUMOUNT("/usr/bin/pumount")
avoid blocking LIRC & Joystick Menu
Definition: mythsystem.h:37