17 #include <sys/types.h>
20 #include <sys/param.h>
28 #include <QDBusConnection>
31 #include <QTextStream>
38 #include "mythconfig.h"
53 #ifndef MNTTYPE_ISO9660
55 #define MNTTYPE_ISO9660 "iso9660"
56 #elif defined(__FreeBSD__) || CONFIG_DARWIN || defined(__OpenBSD__)
57 #define MNTTYPE_ISO9660 "cd9660"
62 #define MNTTYPE_UDF "udf"
66 #define MNTTYPE_AUTO "auto"
69 #ifndef MNTTYPE_SUPERMOUNT
70 #define MNTTYPE_SUPERMOUNT "supermount"
76 #define UDISKS_SVC "org.freedesktop.UDisks"
77 #define UDISKS_PATH "/org/freedesktop/UDisks"
78 #define UDISKS_IFACE "org.freedesktop.UDisks"
79 #define UDISKS_DEVADD "DeviceAdded"
80 #define UDISKS_DEVRMV "DeviceRemoved"
81 #define UDISKS_DEVSIG "o" // OBJECT_PATH
83 #define UDISKS2_SVC "org.freedesktop.UDisks2"
84 #define UDISKS2_PATH "/org/freedesktop/UDisks2"
85 #define UDISKS2_IFACE "org.freedesktop.UDisks2.Drive"
93 static const QString
LOC = QString(
"MMUnix:");
99 LOG(VB_GENERAL, LOG_ALERT,
100 LOC + methodName +
" Error: failed to open " + _PATH_FSTAB +
101 " for reading, " +
ENO);
105 static void statError(
const QString &methodName,
const QString &devPath)
107 LOG(VB_GENERAL, LOG_ALERT,
108 LOC + methodName +
" Error: failed to stat " + devPath +
117 unsigned long interval,
bool allowEject)
122 LOG(VB_GENERAL, LOG_NOTICE,
"MediaMonitor disabled by user setting.");
129 LOG(VB_MEDIA, LOG_INFO,
"Initial device list...\n" +
listDevices());
144 #endif // !CONFIG_QTDBUS
151 struct fstab * mep =
nullptr;
161 while ((mep = getfsent()) !=
nullptr)
174 static QVariant DeviceProperty(
const QDBusObjectPath& o,
const std::string& kszProperty)
178 QDBusInterface iface(UDISKS_SVC, o.path(), UDISKS_IFACE
".Device",
179 QDBusConnection::systemBus() );
181 v = iface.property(kszProperty.c_str());
201 for (
int i = 0; i < 10; ++i, usleep(500000))
205 QDBusInterface iface(UDISKS_SVC, UDISKS_PATH, UDISKS_IFACE,
206 QDBusConnection::systemBus() );
207 if (!iface.isValid())
209 LOG(VB_GENERAL, LOG_ALERT,
LOC +
210 "CheckMountable: DBus interface error: " +
211 iface.lastError().message() );
212 QDBusInterface iface2(UDISKS2_SVC, UDISKS2_PATH, UDISKS2_IFACE,
213 QDBusConnection::systemBus() );
214 if (iface2.isValid()) {
217 LOG(VB_GENERAL, LOG_WARNING,
LOC +
218 "UDisks2 service found. Media Monitor does not support this yet!");
225 using QDBusObjectPathList = QList<QDBusObjectPath>;
226 QDBusReply<QDBusObjectPathList> reply = iface.call(
"EnumerateDevices");
227 if (!reply.isValid())
229 LOG(VB_GENERAL, LOG_ALERT,
LOC +
230 "CheckMountable DBus EnumerateDevices error: " +
231 reply.error().message() );
236 (void)QDBusConnection::systemBus().connect(
237 UDISKS_SVC, UDISKS_PATH, UDISKS_IFACE, UDISKS_DEVADD, UDISKS_DEVSIG,
238 this, SLOT(deviceAdded(QDBusObjectPath)) );
239 (void)QDBusConnection::systemBus().connect(
240 UDISKS_SVC, UDISKS_PATH, UDISKS_IFACE, UDISKS_DEVRMV, UDISKS_DEVSIG,
241 this, SLOT(deviceRemoved(QDBusObjectPath)) );
244 const QDBusObjectPathList& list(reply.value());
245 for (
const auto& entry : qAsConst(list))
247 if (!DeviceProperty(entry,
"DeviceIsSystemInternal").toBool() &&
248 !DeviceProperty(entry,
"DeviceIsPartitionTable").toBool() )
250 QString dev = DeviceProperty(entry,
"DeviceFile").toString();
253 if (dev.startsWith(
"/dev/fd"))
257 if (DeviceProperty(entry,
"DeviceIsRemovable").toBool())
260 pDevice =
MythHDD::Get(
this, dev.toLatin1(),
false,
false);
263 pDevice->deleteLater();
279 QDir sysfs(
"/sys/block");
280 sysfs.setFilter(QDir::Dirs | QDir::NoDotAndDotDot);
282 for (
const auto& device : sysfs.entryList())
285 if (device.startsWith(
"fd"))
289 QString path = sysfs.absolutePath();
307 QString removablePath = dev +
"/removable";
308 QFile removable(removablePath);
309 if (removable.exists() && removable.open(QIODevice::ReadOnly))
312 QString msg =
LOC +
":CheckRemovable(" + dev +
")/removable ";
313 bool ok = removable.getChar(&c);
318 LOG(VB_MEDIA, LOG_DEBUG, msg + c);
324 LOG(VB_GENERAL, LOG_ALERT, msg +
"failed");
340 QString msg =
LOC +
":GetDeviceFile(" + sysfs +
")";
344 ret.replace(QRegExp(
".*/"),
"/dev/");
350 struct udev *udev = udev_new();
353 struct udev_device *device =
354 udev_device_new_from_syspath(udev, sysfs.toLatin1().constData());
355 if (device !=
nullptr)
357 const char *name = udev_device_get_devnode(device);
367 LOG(VB_MEDIA, LOG_DEBUG, msg +
" devnode not (yet) known");
370 udev_device_unref(device);
374 LOG(VB_GENERAL, LOG_ALERT,
375 msg +
" udev_device_new_from_syspath returned NULL");
382 LOG(VB_GENERAL, LOG_ALERT,
383 "MediaMonitorUnix::GetDeviceFile udev_new failed");
384 #else // HAVE_LIBUDEV
387 args <<
"info" <<
"-q" <<
"name"
406 while( !estream.atEnd() )
407 LOG(VB_MEDIA, LOG_DEBUG,
408 msg +
" - udevadm info error...\n" + estream.readLine());
411 QTextStream ostream(udevinfo->
ReadAll());
412 QString udevLine = ostream.readLine();
413 if (!udevLine.startsWith(
"device not found in database") )
417 #endif // HAVE_LIBUDEV
420 LOG(VB_MEDIA, LOG_INFO, msg +
"->'" + ret +
"'");
423 #endif // !CONFIG_QTDBUS
435 QDBusInterface iface(UDISKS_SVC, UDISKS_PATH, UDISKS_IFACE,
436 QDBusConnection::systemBus() );
440 using QDBusObjectPathList = QList<QDBusObjectPath>;
441 QDBusReply<QDBusObjectPathList> reply = iface.call(
"EnumerateDevices");
444 const QDBusObjectPathList& list(reply.value());
445 for (
const auto& entry : qAsConst(list))
447 if (DeviceProperty(entry,
"DeviceIsRemovable").toBool())
449 QString dev = DeviceProperty(entry,
"DeviceFile").toString();
450 if (dev.startsWith(
"/dev/"))
459 QFile
file(
"/proc/sys/dev/cdrom/info");
460 if (
file.open(QIODevice::ReadOnly))
463 QTextStream stream(&
file);
466 line = stream.readLine();
467 if (line.startsWith(
"drive name:"))
469 #if QT_VERSION < QT_VERSION_CHECK(5,14,0)
470 l = line.split(
'\t', QString::SkipEmptyParts);
472 l = line.split(
'\t', Qt::SkipEmptyParts);
478 while (!stream.atEnd());
483 LOG(VB_MEDIA, LOG_DEBUG,
484 LOC +
":GetCDROMBlockDevices()->'" + l.join(
", ") +
"'");
493 QDBusInterface iface(UDISKS_SVC, UDISKS_PATH, UDISKS_IFACE,
494 QDBusConnection::systemBus() );
497 QDBusReply<QDBusObjectPath> reply = iface.call(
501 desc = DeviceProperty(reply,
"DriveVendor").toString();
504 desc += DeviceProperty(reply,
"DriveModel").toString();
513 if (devname.startsWith(
"hd"))
515 QFile
file(
"/proc/ide/" + devname.left(3) +
"/model");
516 if (
file.open(QIODevice::ReadOnly))
518 QTextStream stream(&
file);
520 desc.append(stream.readLine());
525 if (devname.startsWith(
"scd"))
526 devname.replace(
"scd",
"sr");
528 if (devname.startsWith(
"sd")
529 || devname.startsWith(
"sr"))
531 QString path = devname.prepend(
"/sys/block/");
532 path.append(
"/device/");
534 QFile
file(path +
"vendor");
535 if (
file.open(QIODevice::ReadOnly))
537 QTextStream stream(&
file);
539 desc.append(stream.readLine());
544 file.setFileName(path +
"model");
545 if (
file.open(QIODevice::ReadOnly))
547 QTextStream stream(&
file);
549 desc.append(stream.readLine());
556 LOG(VB_MEDIA, LOG_DEBUG, QString(
"LookupModel '%1' -> '%2'")
568 LOG(VB_GENERAL, LOG_ERR,
"MediaMonitorUnix::AddDevice(null)");
579 LOG(VB_GENERAL, LOG_ALERT,
580 "MediaMonitorUnix::AddDevice() - empty device path.");
585 if (stat(path.toLocal8Bit().constData(), &sb) < 0)
590 dev_t new_rdev = sb.st_rdev;
595 for (
const auto *device : qAsConst(
m_devices))
597 if (stat(device->getDevicePath().toLocal8Bit().constData(), &sb) < 0)
599 statError(
":AddDevice()", device->getDevicePath());
603 if (sb.st_rdev == new_rdev)
605 LOG(VB_MEDIA, LOG_INFO,
606 LOC +
":AddDevice() - not adding " + path +
608 "because it appears to be a duplicate of " +
609 device->getDevicePath());
622 LOG(VB_MEDIA, LOG_INFO,
LOC +
":AddDevice() - Added " + path);
635 QString devicePath( mep->fs_spec );
636 LOG(VB_GENERAL, LOG_DEBUG,
"AddDevice - " + devicePath);
642 bool is_supermount =
false;
643 bool is_cdrom =
false;
645 if (stat(mep->fs_spec, &sbuf) < 0)
649 if ( ! ( ((strstr(mep->fs_mntops,
"owner") &&
650 (sbuf.st_mode & S_IRUSR)) || strstr(mep->fs_mntops,
"user")) &&
651 (strstr(mep->fs_vfstype, MNTTYPE_ISO9660) ||
655 if (strstr(mep->fs_mntops, MNTTYPE_ISO9660) &&
658 is_supermount =
true;
666 if (strstr(mep->fs_mntops, MNTTYPE_ISO9660) ||
667 strstr(mep->fs_vfstype, MNTTYPE_ISO9660) ||
673 LOG(VB_GENERAL, LOG_DEBUG,
"Device is a CDROM");
685 QString dev(mep->fs_mntops);
686 int pos = dev.indexOf(QString::fromStdString(
kSuperOptDev));
690 #if QT_VERSION < QT_VERSION_CHECK(5,14,0)
691 QStringList parts = dev.split(QRegularExpression(
"[, ]"),
692 QString::SkipEmptyParts);
694 QStringList parts = dev.split(QRegularExpression(
"[, ]"),
697 if (parts[0].isEmpty())
710 pDevice->deleteLater();
721 void MediaMonitorUnix::deviceAdded(
const QDBusObjectPath& o)
723 LOG(VB_MEDIA, LOG_INFO,
LOC +
":deviceAdded " + o.path());
726 if (!DeviceProperty(o,
"DeviceIsPartitionTable").toBool())
728 QString dev = DeviceProperty(o,
"DeviceFile").toString();
731 if (DeviceProperty(o,
"DeviceIsRemovable").toBool())
734 pDevice =
MythHDD::Get(
this, dev.toLatin1(),
false,
false);
737 pDevice->deleteLater();
744 void MediaMonitorUnix::deviceRemoved(
const QDBusObjectPath& o)
746 LOG(VB_MEDIA, LOG_INFO,
LOC +
"deviceRemoved " + o.path());
747 #if 0 // This fails because the DeviceFile has just been deleted
748 QString dev = DeviceProperty(o,
"DeviceFile");
752 QString dev = QFileInfo(o.path()).baseName();
753 dev.prepend(
"/dev/");
758 #else //CONFIG_QTDBUS
778 LOG(VB_MEDIA, LOG_DEBUG,
779 LOC +
":FindPartitions(" + dev +
780 QString(
",%1").
arg(checkPartitions ?
" true" :
" false" ) +
")");
787 sysfs.setFilter(QDir::Dirs | QDir::NoDotAndDotDot);
789 bool found_partitions =
false;
790 QStringList parts = sysfs.entryList();
791 for (
const auto& part : qAsConst(parts))
794 if (part ==
"device" || part ==
"holders" || part ==
"queue"
795 || part ==
"slaves" || part ==
"subsystem"
796 || part ==
"bdi" || part ==
"power")
800 sysfs.absoluteFilePath(part),
false);
804 if (!found_partitions)
807 return found_partitions;
812 if (device_file.isEmpty())
817 if (cdroms.contains(dev.section(
'/', -1)))
821 this, device_file.toLatin1().constData(),
false,
m_allowEject);
827 this, device_file.toLatin1().constData(),
false,
false);
834 pDevice->deleteLater();
857 qBuffer.append(buffer);
860 #if QT_VERSION < QT_VERSION_CHECK(5,14,0)
861 const QStringList list = qBuffer.split(
'\n', QString::SkipEmptyParts);
863 const QStringList list = qBuffer.split(
'\n', Qt::SkipEmptyParts);
865 for (
const auto& notif : qAsConst(list))
867 if (notif.startsWith(
"add"))
869 QString dev = notif.section(
' ', 1, 1);
870 LOG(VB_MEDIA, LOG_INFO,
"Udev add " + dev);
875 else if (notif.startsWith(
"remove"))
877 QString dev = notif.section(
' ', 2, 2);
878 LOG(VB_MEDIA, LOG_INFO,
"Udev remove " + dev);