/** * \brief Deal with the user, or another program, renaming a volume * * iTunes has a habit of renaming the disk volumes and track files * after it looks up a disk on the GraceNote CDDB. */ void MonitorThreadDarwin::diskRename(const char *devName, const char *volName) { LOG(VB_MEDIA, LOG_DEBUG, QString("MonitorThreadDarwin::diskRename(%1,%2)") .arg(devName).arg(volName)); MythMediaDevice *pDevice = m_Monitor->GetMedia(devName); if (m_Monitor->ValidateAndLock(pDevice)) { // Send message to plugins to ignore this drive: if (m_Monitor->m_SendEvent) pDevice->setStatus(MEDIASTAT_NODISK); pDevice->setVolumeID(volName); pDevice->setMountPath((QString("/Volumes/") + volName).toLatin1()); // Plugins can now use it again: if (m_Monitor->m_SendEvent) pDevice->setStatus(MEDIASTAT_USEABLE); m_Monitor->Unlock(pDevice); } else LOG(VB_MEDIA, LOG_INFO, QString("Couldn't find MythMediaDevice: %1").arg(devName)); }
void Ripper::ejectCD() { LOG(VB_MEDIA, LOG_INFO, __PRETTY_FUNCTION__); bool bEjectCD = gCoreContext->GetNumSetting("EjectCDAfterRipping",1); if (bEjectCD) { #ifdef HAVE_CDIO LOG(VB_MEDIA, LOG_INFO, QString("Ripper::%1 '%2'"). arg(__func__).arg(m_CDdevice)); (void)cdio_eject_media_drive(m_CDdevice.toLatin1().constData()); #else MediaMonitor *mon = MediaMonitor::GetMediaMonitor(); if (mon) { QByteArray devname = m_CDdevice.toLatin1(); MythMediaDevice *pMedia = mon->GetMedia(devname.constData()); if (pMedia && mon->ValidateAndLock(pMedia)) { pMedia->eject(); mon->Unlock(pMedia); } } #endif // HAVE_CDIO } }
/** * \copydoc MythUIType::mediaEvent() */ void MythThemedMenu::mediaEvent(MythMediaEvent* event) { if (!event) return; MythMediaDevice *device = event->getDevice(); if (!device) return; MythMediaType type = device->getMediaType(); MythMediaStatus status = device->getStatus(); if ((status & ~MEDIASTAT_USEABLE) && (status & ~MEDIASTAT_MOUNTED)) return; switch (type) { case MEDIATYPE_DVD : // DVD Available break; case MEDIATYPE_BD : // Blu-ray Available break; default : return; } }
void MediaMonitor::SetCDSpeed(const char *device, int speed) { MediaMonitor *mon = GetMediaMonitor(); if (mon) { MythMediaDevice *pMedia = mon->GetMedia(device); if (pMedia && mon->ValidateAndLock(pMedia)) { pMedia->setSpeed(speed); mon->Unlock(pMedia); return; } } MythCDROM *cd = MythCDROM::get(NULL, device, false, false); if (cd) { cd->setDeviceSpeed(device, speed); delete cd; return; } LOG(VB_MEDIA, LOG_INFO, QString("MediaMonitor::setSpeed(%1) - Cannot find/create CDROM?") .arg(device)); }
/** * If the device is being monitored, return its mountpoint. * * A convenience function for plugins. * (Only currently needed for Mac OS X, which mounts Audio CDs) */ QString MediaMonitor::GetMountPath(const QString& devPath) { QString mountPath; if (c_monitor) { MythMediaDevice *pMedia = c_monitor->GetMedia(devPath); if (pMedia && c_monitor->ValidateAndLock(pMedia)) { mountPath = pMedia->getMountPath(); c_monitor->Unlock(pMedia); } // The media monitor could be inactive. // Create a fake media device just to lookup mount map: else { pMedia = MythCDROM::get(NULL, devPath.toLatin1(), true, false); if (pMedia && pMedia->findMountPath()) mountPath = pMedia->getMountPath(); else LOG(VB_MEDIA, LOG_INFO, "MediaMonitor::GetMountPath() - failed"); // need some way to delete the media device. } } return mountPath; }
void Ripper::ejectCD() { bool bEjectCD = gCoreContext->GetNumSetting("EjectCDAfterRipping",1); if (bEjectCD) { #ifdef HAVE_CDAUDIO QByteArray devname = m_CDdevice.toAscii(); int cdrom_fd = cd_init_device(const_cast<char*>(devname.constData())); VERBOSE(VB_MEDIA, "Ripper::ejectCD() - dev " + m_CDdevice); if (cdrom_fd != -1) { if (cd_eject(cdrom_fd) == -1) perror("Failed on cd_eject"); cd_finish(cdrom_fd); } else perror("Failed on cd_init_device"); #else MediaMonitor *mon = MediaMonitor::GetMediaMonitor(); if (mon) { QByteArray devname = m_CDdevice.toAscii(); MythMediaDevice *pMedia = mon->GetMedia(devname.constData()); if (pMedia && mon->ValidateAndLock(pMedia)) { pMedia->eject(); mon->Unlock(pMedia); } } #endif } }
/** * Installed into the main window's event chain, * so that the main thread can safely jump to plugin code. */ bool MediaMonitor::eventFilter(QObject *obj, QEvent *event) { if (event->type() == MythMediaEvent::kEventType) { MythMediaDevice *pDev = ((MythMediaEvent*)event)->getDevice(); if (!pDev) { VERBOSE(VB_IMPORTANT, "MediaMonitor::eventFilter() got a bad media event?"); return true; } if (pDev->isUsable()) JumpToMediaHandler(pDev); else { // We don't want to jump around in the menus, but should // call each plugin's callback so it can track this change. QMap<QString, MHData>::Iterator itr = m_handlerMap.begin(); while (itr != m_handlerMap.end()) { if ((*itr).MythMediaType & (int)pDev->getMediaType()) (*itr).callback(pDev); itr++; } } return false; // Don't eat the event } // standard event processing return QObject::eventFilter(obj, event); }
/** \fn MediaMonitor::CheckDevices(void) * \brief Poll the devices in our list. */ void MediaMonitor::CheckDevices(void) { /* check if new devices have been plugged in */ CheckDeviceNotifications(); QList<MythMediaDevice*>::iterator itr = m_Devices.begin(); MythMediaDevice* pDev; while (itr != m_Devices.end()) { pDev = *itr; if (pDev) pDev->checkMedia(); ++itr; } }
void MonitorThreadDarwin::diskRemove(QString devName) { LOG(VB_MEDIA, LOG_DEBUG, QString("MonitorThreadDarwin::diskRemove(%1)").arg(devName)); if (m_Monitor->m_SendEvent) { MythMediaDevice *pDevice = m_Monitor->GetMedia(devName); if (pDevice) // Probably should ValidateAndLock() here? pDevice->setStatus(MEDIASTAT_NODISK); else LOG(VB_MEDIA, LOG_INFO, "Couldn't find MythMediaDevice: " + devName); } m_Monitor->RemoveDevice(devName); }
/** * \class MediaMonitorWindows * * I am assuming, for now, that everything on Windows uses drive letters * (e.g. C:, D:). That is probably wrong, though. (other APIs?) */ MediaMonitorWindows::MediaMonitorWindows(QObject* par, unsigned long interval, bool allowEject) : MediaMonitor(par, interval, allowEject) { char strDrives[128]; if (!::GetLogicalDriveStrings(sizeof(strDrives), strDrives)) return; for (char *driveName = strDrives; *driveName; driveName += strlen(driveName) + 1) { uint type = ::GetDriveType(driveName); if (type != DRIVE_REMOVABLE && type != DRIVE_CDROM) continue; MythMediaDevice *media = NULL; if (type == DRIVE_CDROM) media = MythCDROM::get(this, driveName, false, allowEject); else media = MythHDD::Get(this, driveName, false, allowEject); if (!media) { VERBOSE(VB_IMPORTANT, "Error. Couldn't create MythMediaDevice."); return; } // We store the volume name to improve // user activities like ChooseAndEjectMedia(). char volumeName[MAX_PATH]; if (GetVolumeInformation(driveName, volumeName, MAX_PATH, NULL, NULL, NULL, NULL, NULL)) { media->setVolumeID(volumeName); } AddDevice(media); } VERBOSE(VB_MEDIA, "Initial device list: " + listDevices()); }
/* * DBus UDisk AddDevice handler */ void MediaMonitorUnix::deviceAdded( QDBusObjectPath o) { LOG(VB_MEDIA, LOG_INFO, LOC + ":deviceAdded " + o.path()); // Don't add devices with partition tables, just the partitions if (!DeviceProperty(o, "DeviceIsPartitionTable").toBool()) { QString dev = DeviceProperty(o, "DeviceFile").toString(); MythMediaDevice* pDevice; if (DeviceProperty(o, "DeviceIsRemovable").toBool()) pDevice = MythCDROM::get(this, dev.toLatin1(), false, m_AllowEject); else pDevice = MythHDD::Get(this, dev.toLatin1(), false, false); if (pDevice && !AddDevice(pDevice)) pDevice->deleteLater(); } }
QString MediaMonitor::defaultDevice(QString dbSetting, QString label, const char *hardCodedDefault) { QString device = gCoreContext->GetSetting(dbSetting); LOG(VB_MEDIA, LOG_DEBUG, QString("MediaMonitor::defaultDevice(%1,..,%2) dbSetting='%3'") .arg(dbSetting).arg(hardCodedDefault).arg(device)); // No settings database defaults? Try to choose one: if (device.isEmpty() || device == "default") { device = hardCodedDefault; if (!c_monitor) c_monitor = GetMediaMonitor(); if (c_monitor) { MythMediaDevice *d = c_monitor->selectDrivePopup(label, false, true); if (d == (MythMediaDevice *) -1) // User cancelled { device.clear(); // If user has explicitly cancelled return empty string d = NULL; } if (d && c_monitor->ValidateAndLock(d)) { device = d->getDevicePath(); c_monitor->Unlock(d); } } } LOG(VB_MEDIA, LOG_DEBUG, "MediaMonitor::defaultDevice() returning " + device); return device; }
/** * \brief Create a MythMedia instance and insert in MythMediaMonitor list * * We are a friend class of MythMediaMonitor, * so that we can add or remove from its list of media objects. */ void MonitorThreadDarwin::diskInsert(const char *devName, const char *volName, QString model, bool isCDorDVD) { MythMediaDevice *media; QString msg = "MonitorThreadDarwin::diskInsert"; LOG(VB_MEDIA, LOG_DEBUG, msg + QString("(%1,%2,'%3',%4)") .arg(devName).arg(volName).arg(model).arg(isCDorDVD)); if (isCDorDVD) media = MythCDROM::get(NULL, devName, true, m_Monitor->m_AllowEject); else media = MythHDD::Get(NULL, devName, true, false); if (!media) { LOG(VB_GENERAL, LOG_ALERT, msg + "Couldn't create MythMediaDevice."); return; } // We store the volume name for user activities like ChooseAndEjectMedia(). media->setVolumeID(volName); media->setDeviceModel(model.toLatin1()); // Same for the Manufacturer and model // Mac OS X devices are pre-mounted here: QString mnt = "/Volumes/"; mnt += volName; media->setMountPath(mnt.toLatin1()); int attempts = 0; QDir d(mnt); while (!d.exists()) { LOG(VB_MEDIA, LOG_WARNING, (msg + "() - Waiting for mount '%1' to become stable.").arg(mnt)); usleep(120000); if ( ++attempts > 4 ) usleep(200000); if ( attempts > 8 ) { delete media; LOG(VB_MEDIA, LOG_ALERT, msg + "() - Giving up"); return; } } media->setStatus(MEDIASTAT_MOUNTED); // This is checked in AddDevice(), but checking earlier means // we can avoid scanning all the files to determine its type if (m_Monitor->shouldIgnore(media)) return; // We want to use MythMedia's code to work out the mediaType. // media->onDeviceMounted() is protected, // so to call it indirectly, we pretend to mount it here. media->mount(); m_Monitor->AddDevice(media); }
QString MediaMonitor::defaultDevice(QString dbSetting, QString label, const char *hardCodedDefault) { QString device = gCoreContext->GetSetting(dbSetting); VERBOSE(VB_MEDIA+VB_EXTRA, QString("MediaMonitor::defaultDevice(%1,..,%2) dbSetting='%3'") .arg(dbSetting).arg(hardCodedDefault).arg(device)); // No settings database defaults? Try to choose one: if (device.isEmpty() || device == "default") { device = hardCodedDefault; if (!c_monitor) c_monitor = GetMediaMonitor(); if (c_monitor) { MythMediaDevice *d = c_monitor->selectDrivePopup(label); if (d == (MythMediaDevice *) -1) // User cancelled d = NULL; if (d && c_monitor->ValidateAndLock(d)) { device = d->getDevicePath(); c_monitor->Unlock(d); } } } VERBOSE(VB_MEDIA+VB_EXTRA, "MediaMonitor::defaultDevice() returning " + device); return device; }
/** * \brief Search /sys/block for valid removable media devices. * * This function creates MediaDevice instances for valid removable media * devices found under the /sys/block filesystem in Linux. CD and DVD * devices are created as MythCDROM instances. MythHDD instances will be * created for each partition on removable hard disk devices, if they exist. * Otherwise a single MythHDD instance will be created for the entire disc. * * NOTE: Floppy disks are ignored. */ bool MediaMonitorUnix::CheckMountable(void) { #if CONFIG_QTDBUS for (int i = 0; i < 10; ++i, usleep(500000)) { // Connect to UDisks. This can sometimes fail if mythfrontend // is started during system init QDBusInterface iface(UDISKS_SVC, UDISKS_PATH, UDISKS_IFACE, QDBusConnection::systemBus() ); if (!iface.isValid()) { LOG(VB_GENERAL, LOG_ALERT, LOC + "CheckMountable: DBus interface error: " + iface.lastError().message() ); continue; } // Enumerate devices typedef QList<QDBusObjectPath> QDBusObjectPathList; QDBusReply<QDBusObjectPathList> reply = iface.call("EnumerateDevices"); if (!reply.isValid()) { LOG(VB_GENERAL, LOG_ALERT, LOC + "CheckMountable DBus EnumerateDevices error: " + reply.error().message() ); continue; } // Listen on DBus for UDisk add/remove device messages (void)QDBusConnection::systemBus().connect( UDISKS_SVC, UDISKS_PATH, UDISKS_IFACE, UDISKS_DEVADD, UDISKS_DEVSIG, this, SLOT(deviceAdded(QDBusObjectPath)) ); (void)QDBusConnection::systemBus().connect( UDISKS_SVC, UDISKS_PATH, UDISKS_IFACE, UDISKS_DEVRMV, UDISKS_DEVSIG, this, SLOT(deviceRemoved(QDBusObjectPath)) ); // Parse the returned device array const QDBusObjectPathList& list(reply.value()); for (QDBusObjectPathList::const_iterator it = list.begin(); it != list.end(); ++it) { if (!DeviceProperty(*it, "DeviceIsSystemInternal").toBool() && !DeviceProperty(*it, "DeviceIsPartitionTable").toBool() ) { QString dev = DeviceProperty(*it, "DeviceFile").toString(); // ignore floppies, too slow if (dev.startsWith("/dev/fd")) continue; MythMediaDevice* pDevice; if (DeviceProperty(*it, "DeviceIsRemovable").toBool()) pDevice = MythCDROM::get(this, dev.toLatin1(), false, m_AllowEject); else pDevice = MythHDD::Get(this, dev.toLatin1(), false, false); if (pDevice && !AddDevice(pDevice)) pDevice->deleteLater(); } } // Success return true; } // Timed out return false; #elif defined linux // NB needs script in /etc/udev/rules.d mkfifo(kUDEV_FIFO, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH); m_fifo = open(kUDEV_FIFO, O_RDONLY | O_NONBLOCK); QDir sysfs("/sys/block"); sysfs.setFilter(QDir::Dirs | QDir::NoDotAndDotDot); QStringList devices = sysfs.entryList(); for (QStringList::iterator it = devices.begin(); it != devices.end(); ++it) { // ignore floppies, too slow if ((*it).startsWith("fd")) continue; sysfs.cd(*it); QString path = sysfs.absolutePath(); if (CheckRemovable(path)) FindPartitions(path, true); sysfs.cdUp(); } return true; #else // linux return false; #endif }
// Given a fstab entry to a media device determine what type of device it is bool MediaMonitorUnix::AddDevice(struct fstab * mep) { if (!mep) return false; #ifndef Q_OS_ANDROID QString devicePath( mep->fs_spec ); #if 0 LOG(VB_GENERAL, LOG_DEBUG, "AddDevice - " + devicePath); #endif MythMediaDevice* pDevice = NULL; struct stat sbuf; bool is_supermount = false; bool is_cdrom = false; if (stat(mep->fs_spec, &sbuf) < 0) return false; // Can it be mounted? if ( ! ( ((strstr(mep->fs_mntops, "owner") && (sbuf.st_mode & S_IRUSR)) || strstr(mep->fs_mntops, "user")) && (strstr(mep->fs_vfstype, MNTTYPE_ISO9660) || strstr(mep->fs_vfstype, MNTTYPE_UDF) || strstr(mep->fs_vfstype, MNTTYPE_AUTO)) ) ) { if (strstr(mep->fs_mntops, MNTTYPE_ISO9660) && strstr(mep->fs_vfstype, MNTTYPE_SUPERMOUNT)) { is_supermount = true; } else { return false; } } if (strstr(mep->fs_mntops, MNTTYPE_ISO9660) || strstr(mep->fs_vfstype, MNTTYPE_ISO9660) || strstr(mep->fs_vfstype, MNTTYPE_UDF) || strstr(mep->fs_vfstype, MNTTYPE_AUTO)) { is_cdrom = true; #if 0 LOG(VB_GENERAL, LOG_DEBUG, "Device is a CDROM"); #endif } if (!is_supermount) { if (is_cdrom) pDevice = MythCDROM::get(this, mep->fs_spec, is_supermount, m_AllowEject); } else { char *dev = 0; int len = 0; dev = strstr(mep->fs_mntops, SUPER_OPT_DEV); if (dev == NULL) return false; dev += sizeof(SUPER_OPT_DEV)-1; while (dev[len] != ',' && dev[len] != ' ' && dev[len] != 0) len++; if (dev[len] != 0) { char devstr[256]; strncpy(devstr, dev, len); devstr[len] = 0; if (is_cdrom) pDevice = MythCDROM::get(this, devstr, is_supermount, m_AllowEject); } else return false; } if (pDevice) { pDevice->setMountPath(mep->fs_file); if (pDevice->testMedia() == MEDIAERR_OK) { if (AddDevice(pDevice)) return true; } pDevice->deleteLater(); } #endif return false; }
/** * \brief Creates MythMedia instances for sysfs removable media devices. * * Block devices are represented as directories in sysfs with directories * for each partition underneath the parent device directory. * * This function recursively calls itself to find all partitions on a block * device and creates a MythHDD instance for each partition found. If no * partitions are found and the device is a CD or DVD device a MythCDROM * instance is created. Otherwise a MythHDD instance is created for the * entire block device. * * \param dev path to sysfs block device. * \param checkPartitions check for partitions on block device. * \return true if MythMedia instances are created. */ bool MediaMonitorUnix::FindPartitions(const QString &dev, bool checkPartitions) { LOG(VB_MEDIA, LOG_DEBUG, LOC + ":FindPartitions(" + dev + QString(",%1").arg(checkPartitions ? " true" : " false" ) + ")"); MythMediaDevice* pDevice = NULL; if (checkPartitions) { // check for partitions QDir sysfs(dev); sysfs.setFilter(QDir::Dirs | QDir::NoDotAndDotDot); bool found_partitions = false; QStringList parts = sysfs.entryList(); for (QStringList::iterator pit = parts.begin(); pit != parts.end(); ++pit) { // skip some sysfs dirs that are _not_ sub-partitions if (*pit == "device" || *pit == "holders" || *pit == "queue" || *pit == "slaves" || *pit == "subsystem" || *pit == "bdi" || *pit == "power") continue; found_partitions |= FindPartitions( sysfs.absoluteFilePath(*pit), false); } // no partitions on block device, use main device if (!found_partitions) found_partitions |= FindPartitions(sysfs.absolutePath(), false); return found_partitions; } QString device_file = GetDeviceFile(dev); if (device_file.isEmpty()) return false; QStringList cdroms = GetCDROMBlockDevices(); if (cdroms.contains(dev.section('/', -1))) { // found cdrom device pDevice = MythCDROM::get( this, device_file.toLatin1().constData(), false, m_AllowEject); } else { // found block or partition device pDevice = MythHDD::Get( this, device_file.toLatin1().constData(), false, false); } if (AddDevice(pDevice)) return true; if (pDevice) pDevice->deleteLater(); return false; }