MythTV  master
mediamonitor-unix.cpp
Go to the documentation of this file.
1 // -*- Mode: c++ -*-
2 #include "config.h"
3 
4 // Standard C headers
5 #include <cstdio>
6 
7 // POSIX headers
8 #include <dirent.h>
9 #include <unistd.h>
10 #include <fcntl.h>
11 #ifndef ANDROID
12 #include <fstab.h>
13 #endif
14 
15 // UNIX System headers
16 #include <sys/file.h>
17 #include <sys/types.h>
18 #include <sys/stat.h>
19 #include <sys/wait.h>
20 #include <sys/param.h>
21 
22 // C++ headers
23 #include <iostream>
24 
25 using namespace std;
26 
27 // Qt headers
28 #if CONFIG_QTDBUS
29 #include <QtDBus>
30 #include <QDBusConnection>
31 #endif
32 #include <QList>
33 #include <QTextStream>
34 #include <QDir>
35 #include <QFile>
36 
37 // MythTV headers
38 #include "mythmediamonitor.h"
39 #include "mediamonitor-unix.h"
40 #include "mythdialogs.h"
41 #include "mythconfig.h"
42 #include "mythcdrom.h"
43 #include "mythhdd.h"
44 #include "mythlogging.h"
45 #include "mythsystemlegacy.h"
46 #include "exitcodes.h"
47 
48 #if HAVE_LIBUDEV
49 extern "C" {
50  #include <libudev.h>
51 }
52 #endif
53 
54 
55 #ifndef MNTTYPE_ISO9660
56 #ifdef linux
57 #define MNTTYPE_ISO9660 "iso9660"
58 #elif defined(__FreeBSD__) || CONFIG_DARWIN || defined(__OpenBSD__)
59 #define MNTTYPE_ISO9660 "cd9660"
60 #endif
61 #endif
62 
63 #ifndef MNTTYPE_UDF
64 #define MNTTYPE_UDF "udf"
65 #endif
66 
67 #ifndef MNTTYPE_AUTO
68 #define MNTTYPE_AUTO "auto"
69 #endif
70 
71 #ifndef MNTTYPE_SUPERMOUNT
72 #define MNTTYPE_SUPERMOUNT "supermount"
73 #endif
74 #define SUPER_OPT_DEV "dev="
75 
76 #if CONFIG_QTDBUS
77 // DBus UDisk service - http://hal.freedesktop.org/docs/udisks/
78 #define UDISKS_SVC "org.freedesktop.UDisks"
79 #define UDISKS_PATH "/org/freedesktop/UDisks"
80 #define UDISKS_IFACE "org.freedesktop.UDisks"
81 #define UDISKS_DEVADD "DeviceAdded"
82 #define UDISKS_DEVRMV "DeviceRemoved"
83 #define UDISKS_DEVSIG "o" // OBJECT_PATH
84 // DBus UDisks2 service - https://udisks.freedesktop.org/
85 #define UDISKS2_SVC "org.freedesktop.UDisks2"
86 #define UDISKS2_PATH "/org/freedesktop/UDisks2"
87 #define UDISKS2_IFACE "org.freedesktop.UDisks2.Drive"
88 #endif
89 
90 const char * MediaMonitorUnix::kUDEV_FIFO = "/tmp/mythtv_media";
91 
92 
93 // Some helpers for debugging:
94 
95 static const QString LOC = QString("MMUnix:");
96 
97 #ifndef Q_OS_ANDROID
98 // TODO: are these used?
99 static void fstabError(const QString &methodName)
100 {
101  LOG(VB_GENERAL, LOG_ALERT,
102  LOC + methodName + " Error: failed to open " + _PATH_FSTAB +
103  " for reading, " + ENO);
104 }
105 #endif
106 
107 static void statError(const QString &methodName, const QString &devPath)
108 {
109  LOG(VB_GENERAL, LOG_ALERT,
110  LOC + methodName + " Error: failed to stat " + devPath +
111  ", " + ENO);
112 }
113 
115 // MediaMonitor
116 
117 
119  unsigned long interval, bool allowEject)
120  : MediaMonitor(par, interval, allowEject), m_fifo(-1)
121 {
123  CheckMountable();
124 
125  LOG(VB_MEDIA, LOG_INFO, "Initial device list...\n" + listDevices());
126 }
127 
128 
129 #if !CONFIG_QTDBUS
131 {
132  if (m_fifo >= 0)
133  {
134  close(m_fifo);
135  m_fifo = -1;
136  unlink(kUDEV_FIFO);
137  }
139 }
140 #endif // !CONFIG_QTDBUS
141 
142 
143 // Loop through the file system table and add any supported devices.
145 {
146 #ifndef Q_OS_ANDROID
147  struct fstab * mep = nullptr;
148 
149  // Attempt to open the file system descriptor entry.
150  if (!setfsent())
151  {
152  fstabError(":CheckFileSystemTable()");
153  return false;
154  }
155 
156  // Add all the entries
157  while ((mep = getfsent()) != nullptr)
158  AddDevice(mep);
159 
160  endfsent();
161 
162  if (m_Devices.isEmpty())
163  return false;
164 
165  return true;
166 #else
167  return false;
168 #endif
169 }
170 
171 #if CONFIG_QTDBUS
172 // Get a device property by name
173 static QVariant DeviceProperty(const QDBusObjectPath& o, const char kszProperty[])
174 {
175  QVariant v;
176 
177  QDBusInterface iface(UDISKS_SVC, o.path(), UDISKS_IFACE".Device",
178  QDBusConnection::systemBus() );
179  if (iface.isValid())
180  v = iface.property(kszProperty);
181 
182  return v;
183 }
184 #endif
185 
198 {
199 #if CONFIG_QTDBUS
200  for (int i = 0; i < 10; ++i, usleep(500000))
201  {
202  // Connect to UDisks. This can sometimes fail if mythfrontend
203  // is started during system init
204  QDBusInterface iface(UDISKS_SVC, UDISKS_PATH, UDISKS_IFACE,
205  QDBusConnection::systemBus() );
206  if (!iface.isValid())
207  {
208  LOG(VB_GENERAL, LOG_ALERT, LOC +
209  "CheckMountable: DBus interface error: " +
210  iface.lastError().message() );
211  QDBusInterface iface2(UDISKS2_SVC, UDISKS2_PATH, UDISKS2_IFACE,
212  QDBusConnection::systemBus() );
213  if (iface2.isValid()) {
214  // We have udisks2 service on this system, give up quickly
215  // TODO: Implement device monitoring via udisks2 service
216  LOG(VB_GENERAL, LOG_WARNING, LOC +
217  "UDisks2 service found. Media Monitor does not support this yet!");
218  return false;
219  } else
220  continue;
221  }
222 
223  // Enumerate devices
224  typedef QList<QDBusObjectPath> QDBusObjectPathList;
225  QDBusReply<QDBusObjectPathList> reply = iface.call("EnumerateDevices");
226  if (!reply.isValid())
227  {
228  LOG(VB_GENERAL, LOG_ALERT, LOC +
229  "CheckMountable DBus EnumerateDevices error: " +
230  reply.error().message() );
231  continue;
232  }
233 
234  // Listen on DBus for UDisk add/remove device messages
235  (void)QDBusConnection::systemBus().connect(
236  UDISKS_SVC, UDISKS_PATH, UDISKS_IFACE, UDISKS_DEVADD, UDISKS_DEVSIG,
237  this, SLOT(deviceAdded(QDBusObjectPath)) );
238  (void)QDBusConnection::systemBus().connect(
239  UDISKS_SVC, UDISKS_PATH, UDISKS_IFACE, UDISKS_DEVRMV, UDISKS_DEVSIG,
240  this, SLOT(deviceRemoved(QDBusObjectPath)) );
241 
242  // Parse the returned device array
243  const QDBusObjectPathList& list(reply.value());
244  for (QDBusObjectPathList::const_iterator it = list.begin();
245  it != list.end(); ++it)
246  {
247  if (!DeviceProperty(*it, "DeviceIsSystemInternal").toBool() &&
248  !DeviceProperty(*it, "DeviceIsPartitionTable").toBool() )
249  {
250  QString dev = DeviceProperty(*it, "DeviceFile").toString();
251 
252  // ignore floppies, too slow
253  if (dev.startsWith("/dev/fd"))
254  continue;
255 
256  MythMediaDevice* pDevice;
257  if (DeviceProperty(*it, "DeviceIsRemovable").toBool())
258  pDevice = MythCDROM::get(this, dev.toLatin1(), false, m_AllowEject);
259  else
260  pDevice = MythHDD::Get(this, dev.toLatin1(), false, false);
261 
262  if (pDevice && !AddDevice(pDevice))
263  pDevice->deleteLater();
264  }
265  }
266 
267  // Success
268  return true;
269  }
270 
271  // Timed out
272  return false;
273 
274 #elif defined linux
275  // NB needs script in /etc/udev/rules.d
276  mkfifo(kUDEV_FIFO, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);
277  m_fifo = open(kUDEV_FIFO, O_RDONLY | O_NONBLOCK);
278 
279  QDir sysfs("/sys/block");
280  sysfs.setFilter(QDir::Dirs | QDir::NoDotAndDotDot);
281 
282  QStringList devices = sysfs.entryList();
283 
284  for (QStringList::iterator it = devices.begin(); it != devices.end(); ++it)
285  {
286  // ignore floppies, too slow
287  if ((*it).startsWith("fd"))
288  continue;
289 
290  sysfs.cd(*it);
291  QString path = sysfs.absolutePath();
292  if (CheckRemovable(path))
293  FindPartitions(path, true);
294  sysfs.cdUp();
295  }
296  return true;
297 #else // linux
298  return false;
299 #endif
300 }
301 
302 #if !CONFIG_QTDBUS
303 
306 bool MediaMonitorUnix::CheckRemovable(const QString &dev)
307 {
308 #ifdef linux
309  QString removablePath = dev + "/removable";
310  QFile removable(removablePath);
311  if (removable.exists() && removable.open(QIODevice::ReadOnly))
312  {
313  char c = 0;
314  QString msg = LOC + ":CheckRemovable(" + dev + ")/removable ";
315  bool ok = removable.getChar(&c);
316  removable.close();
317 
318  if (ok)
319  {
320  LOG(VB_MEDIA, LOG_DEBUG, msg + c);
321  if (c == '1')
322  return true;
323  }
324  else
325  {
326  LOG(VB_GENERAL, LOG_ALERT, msg + "failed");
327  }
328  }
329  return false;
330 #else // if !linux
331  return false;
332 #endif // !linux
333 }
334 
340 QString MediaMonitorUnix::GetDeviceFile(const QString &sysfs)
341 {
342  QString msg = LOC + ":GetDeviceFile(" + sysfs + ")";
343  QString ret = sysfs;
344 
345  // In case of error, a working default? (device names usually match)
346  ret.replace(QRegExp(".*/"), "/dev/");
347 
348 #ifdef linux
349  #if HAVE_LIBUDEV
350  // Use libudev to determine the name
351  ret.clear();
352  struct udev *udev = udev_new();
353  if (udev != nullptr)
354  {
355  struct udev_device *device =
356  udev_device_new_from_syspath(udev, sysfs.toLatin1().constData());
357  if (device != nullptr)
358  {
359  const char *name = udev_device_get_devnode(device);
360 
361  if (name != nullptr)
362  ret = tr(name);
363  else
364  {
365  // This can happen when udev sends an AddDevice for a block
366  // device with partitions. FindPartition locates a partition
367  // in sysfs but udev hasn't created the devnode for it yet.
368  // Udev will send another AddDevice for the partition later.
369  LOG(VB_MEDIA, LOG_DEBUG, msg + " devnode not (yet) known");
370  }
371 
372  udev_device_unref(device);
373  }
374  else
375  {
376  LOG(VB_GENERAL, LOG_ALERT,
377  msg + " udev_device_new_from_syspath returned NULL");
378  ret = "";
379  }
380 
381  udev_unref(udev);
382  }
383  else
384  LOG(VB_GENERAL, LOG_ALERT,
385  "MediaMonitorUnix::GetDeviceFile udev_new failed");
386  #else // HAVE_LIBUDEV
387  // Use udevadm info to determine the name
388  QStringList args;
389  args << "info" << "-q" << "name"
390  << "-rp" << sysfs;
391 
392  uint flags = kMSStdOut;
393  if (VERBOSE_LEVEL_CHECK(VB_MEDIA, LOG_DEBUG))
394  flags |= kMSStdErr;
395 
396  // TODO: change this to a MythSystemLegacy on the stack?
397  MythSystemLegacy *udevinfo = new MythSystemLegacy("udevinfo", args, flags);
398  udevinfo->Run(4);
399  if( udevinfo->Wait() != GENERIC_EXIT_OK )
400  {
401  delete udevinfo;
402  return ret;
403  }
404 
405  if (VERBOSE_LEVEL_CHECK(VB_MEDIA, LOG_DEBUG))
406  {
407  QTextStream estream(udevinfo->ReadAllErr());
408  while( !estream.atEnd() )
409  LOG(VB_MEDIA, LOG_DEBUG,
410  msg + " - udevadm info error...\n" + estream.readLine());
411  }
412 
413  QTextStream ostream(udevinfo->ReadAll());
414  QString udevLine = ostream.readLine();
415  if (!udevLine.startsWith("device not found in database") )
416  ret = udevLine;
417 
418  delete udevinfo;
419  #endif // HAVE_LIBUDEV
420 #endif // linux
421 
422  LOG(VB_MEDIA, LOG_INFO, msg + "->'" + ret + "'");
423  return ret;
424 }
425 #endif // !CONFIG_QTDBUS
426 
427 /*
428  * \brief Reads the list devices known to be CD or DVD devices.
429  * \return list of CD and DVD device names.
430  */
431 // pure virtual
433 {
434  QStringList l;
435 
436 #if CONFIG_QTDBUS
437  QDBusInterface iface(UDISKS_SVC, UDISKS_PATH, UDISKS_IFACE,
438  QDBusConnection::systemBus() );
439  if (iface.isValid())
440  {
441  // Enumerate devices
442  typedef QList<QDBusObjectPath> QDBusObjectPathList;
443  QDBusReply<QDBusObjectPathList> reply = iface.call("EnumerateDevices");
444  if (reply.isValid())
445  {
446  const QDBusObjectPathList& list(reply.value());
447  for (QDBusObjectPathList::const_iterator it = list.begin();
448  it != list.end(); ++it)
449  {
450  if (DeviceProperty(*it, "DeviceIsRemovable").toBool())
451  {
452  QString dev = DeviceProperty(*it, "DeviceFile").toString();
453  if (dev.startsWith("/dev/"))
454  dev.remove(0,5);
455  l.push_back(dev);
456  }
457  }
458  }
459  }
460 
461 #elif defined linux
462  QFile file("/proc/sys/dev/cdrom/info");
463  if (file.open(QIODevice::ReadOnly))
464  {
465  QString line;
466  QTextStream stream(&file);
467  do
468  {
469  line = stream.readLine();
470  if (line.startsWith("drive name:"))
471  {
472  l = line.split('\t', QString::SkipEmptyParts);
473  l.pop_front(); // Remove 'drive name:' field
474  break; // file should only contain one drive table?
475  }
476  }
477  while (!stream.atEnd());
478  file.close();
479  }
480 #endif // linux
481 
482  LOG(VB_MEDIA, LOG_DEBUG,
483  LOC + ":GetCDROMBlockDevices()->'" + l.join(", ") + "'");
484  return l;
485 }
486 
487 static void LookupModel(MythMediaDevice* device)
488 {
489  QString desc;
490 
491 #if CONFIG_QTDBUS
492  QDBusInterface iface(UDISKS_SVC, UDISKS_PATH, UDISKS_IFACE,
493  QDBusConnection::systemBus() );
494  if (iface.isValid())
495  {
496  QDBusReply<QDBusObjectPath> reply = iface.call(
497  "FindDeviceByDeviceFile", device->getRealDevice());
498  if (reply.isValid())
499  {
500  desc = DeviceProperty(reply, "DriveVendor").toString();
501  if (!desc.isEmpty())
502  desc += " ";
503  desc += DeviceProperty(reply, "DriveModel").toString();
504  }
505  }
506 
507 #elif defined linux
508 
509  // Given something like /dev/hda1, extract hda1
510  QString devname = device->getRealDevice().mid(5,5);
511 
512  if (devname.startsWith("hd")) // IDE drive
513  {
514  QFile file("/proc/ide/" + devname.left(3) + "/model");
515  if (file.open(QIODevice::ReadOnly))
516  {
517  QTextStream stream(&file);
518 
519  desc.append(stream.readLine());
520  file.close();
521  }
522  }
523 
524  if (devname.startsWith("scd")) // scd0 doesn't appear in /sys/block,
525  devname.replace("scd", "sr"); // use sr0 instead
526 
527  if (devname.startsWith("sd") // SATA/USB/FireWire
528  || devname.startsWith("sr")) // SCSI CD-ROM?
529  {
530  QString path = devname.prepend("/sys/block/");
531  path.append("/device/");
532 
533  QFile file(path + "vendor");
534  if (file.open(QIODevice::ReadOnly))
535  {
536  QTextStream stream(&file);
537 
538  desc.append(stream.readLine());
539  desc.append(' ');
540  file.close();
541  }
542 
543  file.setFileName(path + "model");
544  if (file.open(QIODevice::ReadOnly))
545  {
546  QTextStream stream(&file);
547 
548  desc.append(stream.readLine());
549  desc.append(' ');
550  file.close();
551  }
552  }
553 #endif
554 
555  LOG(VB_MEDIA, LOG_DEBUG, QString("LookupModel '%1' -> '%2'")
556  .arg(device->getRealDevice()).arg(desc) );
557  device->setDeviceModel(desc.toLatin1().constData());
558 }
559 
564 {
565  if ( ! pDevice )
566  {
567  LOG(VB_GENERAL, LOG_ERR, "MediaMonitorUnix::AddDevice(null)");
568  return false;
569  }
570 
571  // If the user doesn't want this device to be monitored, stop now:
572  if (shouldIgnore(pDevice))
573  return false;
574 
575  QString path = pDevice->getDevicePath();
576  if (!path.length())
577  {
578  LOG(VB_GENERAL, LOG_ALERT,
579  "MediaMonitorUnix::AddDevice() - empty device path.");
580  return false;
581  }
582 
583  dev_t new_rdev;
584  struct stat sb;
585 
586  if (stat(path.toLocal8Bit().constData(), &sb) < 0)
587  {
588  statError(":AddDevice()", path);
589  return false;
590  }
591  new_rdev = sb.st_rdev;
592 
593  //
594  // Check if this is a duplicate of a device we have already added
595  //
596  QList<MythMediaDevice*>::const_iterator itr = m_Devices.begin();
597  for (; itr != m_Devices.end(); ++itr)
598  {
599  if (stat((*itr)->getDevicePath().toLocal8Bit().constData(), &sb) < 0)
600  {
601  statError(":AddDevice()", (*itr)->getDevicePath());
602  return false;
603  }
604 
605  if (sb.st_rdev == new_rdev)
606  {
607  LOG(VB_MEDIA, LOG_INFO,
608  LOC + ":AddDevice() - not adding " + path +
609  "\n "
610  "because it appears to be a duplicate of " +
611  (*itr)->getDevicePath());
612  return false;
613  }
614  }
615 
616  LookupModel(pDevice);
617 
618  QMutexLocker locker(&m_DevicesLock);
619 
620  connect(pDevice, SIGNAL(statusChanged(MythMediaStatus, MythMediaDevice*)),
622  m_Devices.push_back( pDevice );
623  m_UseCount[pDevice] = 0;
624  LOG(VB_MEDIA, LOG_INFO, LOC + ":AddDevice() - Added " + path);
625 
626  return true;
627 }
628 
629 // Given a fstab entry to a media device determine what type of device it is
630 bool MediaMonitorUnix::AddDevice(struct fstab * mep)
631 {
632  if (!mep)
633  return false;
634 
635 #ifndef Q_OS_ANDROID
636  QString devicePath( mep->fs_spec );
637 #if 0
638  LOG(VB_GENERAL, LOG_DEBUG, "AddDevice - " + devicePath);
639 #endif
640 
641  MythMediaDevice* pDevice = nullptr;
642  struct stat sbuf;
643 
644  bool is_supermount = false;
645  bool is_cdrom = false;
646 
647  if (stat(mep->fs_spec, &sbuf) < 0)
648  return false;
649 
650  // Can it be mounted?
651  if ( ! ( ((strstr(mep->fs_mntops, "owner") &&
652  (sbuf.st_mode & S_IRUSR)) || strstr(mep->fs_mntops, "user")) &&
653  (strstr(mep->fs_vfstype, MNTTYPE_ISO9660) ||
654  strstr(mep->fs_vfstype, MNTTYPE_UDF) ||
655  strstr(mep->fs_vfstype, MNTTYPE_AUTO)) ) )
656  {
657  if (strstr(mep->fs_mntops, MNTTYPE_ISO9660) &&
658  strstr(mep->fs_vfstype, MNTTYPE_SUPERMOUNT))
659  {
660  is_supermount = true;
661  }
662  else
663  {
664  return false;
665  }
666  }
667 
668  if (strstr(mep->fs_mntops, MNTTYPE_ISO9660) ||
669  strstr(mep->fs_vfstype, MNTTYPE_ISO9660) ||
670  strstr(mep->fs_vfstype, MNTTYPE_UDF) ||
671  strstr(mep->fs_vfstype, MNTTYPE_AUTO))
672  {
673  is_cdrom = true;
674 #if 0
675  LOG(VB_GENERAL, LOG_DEBUG, "Device is a CDROM");
676 #endif
677  }
678 
679  if (!is_supermount)
680  {
681  if (is_cdrom)
682  pDevice = MythCDROM::get(this, mep->fs_spec,
683  is_supermount, m_AllowEject);
684  }
685  else
686  {
687  char *dev = nullptr;
688  int len = 0;
689  dev = strstr(mep->fs_mntops, SUPER_OPT_DEV);
690  if (dev == nullptr)
691  return false;
692 
693  dev += sizeof(SUPER_OPT_DEV)-1;
694  while (dev[len] != ',' && dev[len] != ' ' && dev[len] != 0)
695  len++;
696 
697  if (dev[len] != 0)
698  {
699  char devstr[256];
700  strncpy(devstr, dev, len);
701  devstr[len] = 0;
702  if (is_cdrom)
703  pDevice = MythCDROM::get(this, devstr,
704  is_supermount, m_AllowEject);
705  }
706  else
707  return false;
708  }
709 
710  if (pDevice)
711  {
712  pDevice->setMountPath(mep->fs_file);
713  if (pDevice->testMedia() == MEDIAERR_OK)
714  {
715  if (AddDevice(pDevice))
716  return true;
717  }
718  pDevice->deleteLater();
719  }
720 #endif
721 
722  return false;
723 }
724 
725 #if CONFIG_QTDBUS
726 /*
727  * DBus UDisk AddDevice handler
728  */
729 void MediaMonitorUnix::deviceAdded( QDBusObjectPath o)
730 {
731  LOG(VB_MEDIA, LOG_INFO, LOC + ":deviceAdded " + o.path());
732 
733  // Don't add devices with partition tables, just the partitions
734  if (!DeviceProperty(o, "DeviceIsPartitionTable").toBool())
735  {
736  QString dev = DeviceProperty(o, "DeviceFile").toString();
737 
738  MythMediaDevice* pDevice;
739  if (DeviceProperty(o, "DeviceIsRemovable").toBool())
740  pDevice = MythCDROM::get(this, dev.toLatin1(), false, m_AllowEject);
741  else
742  pDevice = MythHDD::Get(this, dev.toLatin1(), false, false);
743 
744  if (pDevice && !AddDevice(pDevice))
745  pDevice->deleteLater();
746  }
747 }
748 
749 /*
750  * DBus UDisk RemoveDevice handler
751  */
752 void MediaMonitorUnix::deviceRemoved( QDBusObjectPath o)
753 {
754  LOG(VB_MEDIA, LOG_INFO, LOC + "deviceRemoved " + o.path());
755 #if 0 // This fails because the DeviceFile has just been deleted
756  QString dev = DeviceProperty(o, "DeviceFile");
757  if (!dev.isEmpty())
758  RemoveDevice(dev);
759 #else
760  QString dev = QFileInfo(o.path()).baseName();
761  dev.prepend("/dev/");
762  RemoveDevice(dev);
763 #endif
764 }
765 
766 #else //CONFIG_QTDBUS
767 
784 bool MediaMonitorUnix::FindPartitions(const QString &dev, bool checkPartitions)
785 {
786  LOG(VB_MEDIA, LOG_DEBUG,
787  LOC + ":FindPartitions(" + dev +
788  QString(",%1").arg(checkPartitions ? " true" : " false" ) + ")");
789  MythMediaDevice* pDevice = nullptr;
790 
791  if (checkPartitions)
792  {
793  // check for partitions
794  QDir sysfs(dev);
795  sysfs.setFilter(QDir::Dirs | QDir::NoDotAndDotDot);
796 
797  bool found_partitions = false;
798  QStringList parts = sysfs.entryList();
799  for (QStringList::iterator pit = parts.begin();
800  pit != parts.end(); ++pit)
801  {
802  // skip some sysfs dirs that are _not_ sub-partitions
803  if (*pit == "device" || *pit == "holders" || *pit == "queue"
804  || *pit == "slaves" || *pit == "subsystem"
805  || *pit == "bdi" || *pit == "power")
806  continue;
807 
808  found_partitions |= FindPartitions(
809  sysfs.absoluteFilePath(*pit), false);
810  }
811 
812  // no partitions on block device, use main device
813  if (!found_partitions)
814  found_partitions |= FindPartitions(sysfs.absolutePath(), false);
815 
816  return found_partitions;
817  }
818 
819  QString device_file = GetDeviceFile(dev);
820 
821  if (device_file.isEmpty())
822  return false;
823 
824  QStringList cdroms = GetCDROMBlockDevices();
825 
826  if (cdroms.contains(dev.section('/', -1)))
827  {
828  // found cdrom device
829  pDevice = MythCDROM::get(
830  this, device_file.toLatin1().constData(), false, m_AllowEject);
831  }
832  else
833  {
834  // found block or partition device
835  pDevice = MythHDD::Get(
836  this, device_file.toLatin1().constData(), false, false);
837  }
838 
839  if (AddDevice(pDevice))
840  return true;
841 
842  if (pDevice)
843  pDevice->deleteLater();
844 
845  return false;
846 }
847 
854 {
855  char buffer[256];
856  QString qBuffer;
857 
858  if (m_fifo == -1)
859  return;
860 
861  int size = read(m_fifo, buffer, 255);
862  while (size > 0)
863  {
864  // append buffer to QString
865  buffer[size] = '\0';
866  qBuffer.append(buffer);
867  size = read(m_fifo, buffer, 255);
868  }
869  const QStringList list = qBuffer.split('\n', QString::SkipEmptyParts);
870 
871  QStringList::const_iterator it = list.begin();
872  for (; it != list.end(); ++it)
873  {
874  if ((*it).startsWith("add"))
875  {
876  QString dev = (*it).section(' ', 1, 1);
877  LOG(VB_MEDIA, LOG_INFO, "Udev add " + dev);
878 
879  if (CheckRemovable(dev))
880  FindPartitions(dev, true);
881  }
882  else if ((*it).startsWith("remove"))
883  {
884  QString dev = (*it).section(' ', 2, 2);
885  LOG(VB_MEDIA, LOG_INFO, "Udev remove " + dev);
886  RemoveDevice(dev);
887  }
888  }
889 }
890 #endif
static MythHDD * Get(QObject *par, const char *devicePath, bool SuperMount, bool AllowEject)
Helper function used to create a new instance of a hard disk device.
Definition: mythhdd.cpp:15
#define mkfifo(path, mode)
Definition: compat.h:220
void Run(time_t timeout=0)
Runs a command inside the /bin/sh shell. Returns immediately.
const QString & getDevicePath() const
Definition: mythmedia.h:61
QMap< MythMediaDevice *, int > m_UseCount
void setDeviceModel(const char *model)
Definition: mythmedia.h:68
allow access to stdout
Definition: mythsystem.h:39
#define O_NONBLOCK
Definition: mythmedia.cpp:25
#define GENERIC_EXIT_OK
Exited with no error.
Definition: exitcodes.h:10
static void LookupModel(MythMediaDevice *device)
bool shouldIgnore(const MythMediaDevice *device)
Check user preferences to see if this device should be monitored.
unsigned int uint
Definition: compat.h:140
#define SUPER_OPT_DEV
QString GetDeviceFile(const QString &sysfs)
Returns the device special file associated with the /sys/block node.
virtual MythMediaError testMedia()
Definition: mythmedia.h:95
def read(device=None, features=[])
Definition: disc.py:35
virtual void deleteLater(void)
bool AddDevice(MythMediaDevice *pDevice) override
Given a media device, add it to our collection.
QList< MythMediaDevice * > m_Devices
#define S_IROTH
Definition: compat.h:217
#define close
Definition: compat.h:16
static void fstabError(const QString &methodName)
#define VERBOSE_LEVEL_CHECK(_MASK_, _LEVEL_)
Definition: mythlogging.h:24
bool FindPartitions(const QString &dev, bool checkPartitions)
Creates MythMedia instances for sysfs removable media devices.
void mediaStatusChanged(MythMediaStatus oldStatus, MythMediaDevice *pMedia)
Slot which is called when the device status changes and posts a media event to the mainwindow.
QByteArray & ReadAll()
void deleteLater(void) override
#define S_IWGRP
Definition: replex.c:54
const char * name
Definition: ParseText.cpp:339
#define MNTTYPE_UDF
#define ENO
This can be appended to the LOG args with "+".
Definition: mythlogging.h:99
const QString & getRealDevice() const
Definition: mythmedia.h:63
allow access to stderr
Definition: mythsystem.h:40
bool CheckMountable(void)
Search /sys/block for valid removable media devices.
bool CheckRemovable(const QString &dev)
Is /sys/block/dev a removable device?
static MythCDROM * get(QObject *par, const char *devicePath, bool SuperMount, bool AllowEject)
Definition: mythcdrom.cpp:35
uint Wait(time_t timeout=0)
#define S_IRGRP
Definition: compat.h:216
bool CheckFileSystemTable(void)
void CheckDeviceNotifications(void) override
Checks the named pipe, kUDEV_FIFO, for hotplug events from the udev system.
bool RemoveDevice(const QString &dev)
Remove a device from the media monitor.
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: mythlogging.h:41
#define MNTTYPE_SUPERMOUNT
#define S_IWOTH
Definition: replex.c:56
static void statError(const QString &methodName, const QString &devPath)
QByteArray & ReadAllErr()
static const QString LOC
#define MNTTYPE_AUTO
MythMediaStatus
Definition: mythmedia.h:12
MediaMonitorUnix(QObject *par, unsigned long interval, bool allowEject)
QStringList GetCDROMBlockDevices(void) override
void setMountPath(const char *path)
Definition: mythmedia.h:59
const QString listDevices(void)
A string summarising the current devices, for debugging.
static const char * kUDEV_FIFO