File indexing completed on 2024-05-12 04:01:51

0001 /*
0002     SPDX-FileCopyrightText: 2005-2007 Kevin Ottens <ervin@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
0005 */
0006 
0007 #include "devicemanager_p.h" //krazy:exclude=includes (devicenotifier.h is the header file for this class)
0008 #include "devicenotifier.h"
0009 
0010 #include "device.h"
0011 #include "device_p.h"
0012 #include "devices_debug.h"
0013 #include "predicate.h"
0014 #include "storageaccess.h"
0015 #include "storagevolume.h"
0016 
0017 #include "ifaces/device.h"
0018 #include "ifaces/devicemanager.h"
0019 
0020 #include "soliddefs_p.h"
0021 
0022 #include <QDir>
0023 #include <QFileInfo>
0024 #include <QLoggingCategory>
0025 
0026 #include <set>
0027 
0028 Q_GLOBAL_STATIC(Solid::DeviceManagerStorage, globalDeviceStorage)
0029 
0030 Solid::DeviceManagerPrivate::DeviceManagerPrivate()
0031     : m_nullDevice(new DevicePrivate(QString()))
0032 {
0033     loadBackends();
0034 
0035     const QList<QObject *> backends = managerBackends();
0036     for (QObject *backend : backends) {
0037         connect(backend, SIGNAL(deviceAdded(QString)), this, SLOT(_k_deviceAdded(QString)));
0038         connect(backend, SIGNAL(deviceRemoved(QString)), this, SLOT(_k_deviceRemoved(QString)));
0039     }
0040 }
0041 
0042 Solid::DeviceManagerPrivate::~DeviceManagerPrivate()
0043 {
0044     const QList<QObject *> backends = managerBackends();
0045     for (QObject *backend : backends) {
0046         disconnect(backend, nullptr, this, nullptr);
0047     }
0048 
0049     // take a copy as m_devicesMap is changed by Solid::DeviceManagerPrivate::_k_destroyed
0050     const auto deviceMap = m_devicesMap;
0051     for (QPointer<DevicePrivate> dev : deviceMap) {
0052         if (!dev.data()->ref.deref()) {
0053             delete dev.data();
0054         }
0055     }
0056 
0057     m_devicesMap.clear();
0058 }
0059 
0060 QList<Solid::Device> Solid::Device::allDevices()
0061 {
0062     QList<Device> list;
0063     const QList<QObject *> backends = globalDeviceStorage->managerBackends();
0064 
0065     for (QObject *backendObj : backends) {
0066         Ifaces::DeviceManager *backend = qobject_cast<Ifaces::DeviceManager *>(backendObj);
0067 
0068         if (backend == nullptr) {
0069             continue;
0070         }
0071 
0072         const QStringList udis = backend->allDevices();
0073         for (const QString &udi : udis) {
0074             list.append(Device(udi));
0075         }
0076     }
0077 
0078     return list;
0079 }
0080 
0081 QList<Solid::Device> Solid::Device::listFromQuery(const QString &predicate, const QString &parentUdi)
0082 {
0083     Predicate p = Predicate::fromString(predicate);
0084 
0085     if (p.isValid()) {
0086         return listFromQuery(p, parentUdi);
0087     } else {
0088         return QList<Device>();
0089     }
0090 }
0091 
0092 QList<Solid::Device> Solid::Device::listFromType(const DeviceInterface::Type &type, const QString &parentUdi)
0093 {
0094     QList<Device> list;
0095     const QList<QObject *> backends = globalDeviceStorage->managerBackends();
0096 
0097     for (QObject *backendObj : backends) {
0098         Ifaces::DeviceManager *backend = qobject_cast<Ifaces::DeviceManager *>(backendObj);
0099 
0100         if (backend == nullptr) {
0101             continue;
0102         }
0103         if (!backend->supportedInterfaces().contains(type)) {
0104             continue;
0105         }
0106 
0107         const QStringList udis = backend->devicesFromQuery(parentUdi, type);
0108         for (const QString &udi : udis) {
0109             list.append(Device(udi));
0110         }
0111     }
0112 
0113     return list;
0114 }
0115 
0116 QList<Solid::Device> Solid::Device::listFromQuery(const Predicate &predicate, const QString &parentUdi)
0117 {
0118     QList<Device> list;
0119     const QSet<DeviceInterface::Type> usedTypes = predicate.usedTypes();
0120     const QList<QObject *> backends = globalDeviceStorage->managerBackends();
0121     for (QObject *backendObj : backends) {
0122         Ifaces::DeviceManager *backend = qobject_cast<Ifaces::DeviceManager *>(backendObj);
0123 
0124         if (backend == nullptr) {
0125             continue;
0126         }
0127 
0128         QStringList udis;
0129         if (predicate.isValid()) {
0130             QSet<DeviceInterface::Type> supportedTypes = backend->supportedInterfaces();
0131             if (supportedTypes.intersect(usedTypes).isEmpty()) {
0132                 continue;
0133             }
0134 
0135             QList<DeviceInterface::Type> sortedTypes = supportedTypes.values();
0136             std::sort(sortedTypes.begin(), sortedTypes.end());
0137             for (DeviceInterface::Type type : std::as_const(sortedTypes)) {
0138                 udis += backend->devicesFromQuery(parentUdi, type);
0139             }
0140         } else {
0141             udis += backend->allDevices();
0142         }
0143 
0144         std::set<QString> seen;
0145         for (const QString &udi : std::as_const(udis)) {
0146             const auto [it, isInserted] = seen.insert(udi);
0147             if (!isInserted) {
0148                 continue;
0149             }
0150             Device dev(udi);
0151 
0152             bool matches = false;
0153 
0154             if (!predicate.isValid()) {
0155                 matches = true;
0156             } else {
0157                 matches = predicate.matches(dev);
0158             }
0159 
0160             if (matches) {
0161                 list.append(dev);
0162             }
0163         }
0164     }
0165 
0166     return list;
0167 }
0168 
0169 Solid::Device Solid::Device::storageAccessFromPath(const QString &path)
0170 {
0171     if (!QFileInfo::exists(path)) {
0172         qCWarning(Frontend::DeviceManager::DEVICEMANAGER).nospace() << "Couldn't get StorageAccess for \"" << path << "\" - File doesn't exist";
0173         return Device();
0174     }
0175     // We ensure file and all mount paths are with trailing dir separators, to avoid false positive matches later
0176     QString trailing_path(path);
0177     if (!trailing_path.endsWith(QDir::separator())) {
0178         trailing_path.append(QDir::separator());
0179     }
0180 
0181     const QList<Device> list = Solid::Device::listFromType(DeviceInterface::Type::StorageAccess);
0182     Device match;
0183     int match_length = 0;
0184     for (const Device &device : list) {
0185         auto storageVolume = device.as<StorageVolume>();
0186         if (storageVolume && storageVolume->usage() != StorageVolume::UsageType::FileSystem) {
0187             continue;
0188         }
0189 
0190         auto storageAccess = device.as<StorageAccess>();
0191         QString mountPath = storageAccess->filePath();
0192         if (!mountPath.endsWith(QDir::separator())) {
0193             mountPath.append(QDir::separator());
0194         }
0195         if (mountPath.size() > match_length && trailing_path.startsWith(mountPath)) {
0196             match_length = mountPath.size();
0197             match = device;
0198         }
0199     }
0200     return match;
0201 }
0202 
0203 Solid::DeviceNotifier *Solid::DeviceNotifier::instance()
0204 {
0205     return globalDeviceStorage->notifier();
0206 }
0207 
0208 void Solid::DeviceManagerPrivate::_k_deviceAdded(const QString &udi)
0209 {
0210     if (m_devicesMap.contains(udi)) {
0211         DevicePrivate *dev = m_devicesMap[udi].data();
0212 
0213         // Ok, this one was requested somewhere was invalid
0214         // and now becomes magically valid!
0215 
0216         if (dev && dev->backendObject() == nullptr) {
0217             dev->setBackendObject(createBackendObject(udi));
0218             Q_ASSERT(dev->backendObject() != nullptr);
0219         }
0220     }
0221 
0222     Q_EMIT deviceAdded(udi);
0223 }
0224 
0225 void Solid::DeviceManagerPrivate::_k_deviceRemoved(const QString &udi)
0226 {
0227     if (m_devicesMap.contains(udi)) {
0228         DevicePrivate *dev = m_devicesMap[udi].data();
0229 
0230         // Ok, this one was requested somewhere was valid
0231         // and now becomes magically invalid!
0232 
0233         if (dev) {
0234             Q_ASSERT(dev->backendObject() != nullptr);
0235             dev->setBackendObject(nullptr);
0236             Q_ASSERT(dev->backendObject() == nullptr);
0237         }
0238     }
0239 
0240     Q_EMIT deviceRemoved(udi);
0241 }
0242 
0243 void Solid::DeviceManagerPrivate::_k_destroyed(QObject *object)
0244 {
0245     QString udi = m_reverseMap.take(object);
0246 
0247     if (!udi.isEmpty()) {
0248         m_devicesMap.remove(udi);
0249     }
0250 }
0251 
0252 Solid::DevicePrivate *Solid::DeviceManagerPrivate::findRegisteredDevice(const QString &udi)
0253 {
0254     if (udi.isEmpty()) {
0255         return m_nullDevice.data();
0256     } else if (m_devicesMap.contains(udi)) {
0257         return m_devicesMap[udi].data();
0258     } else {
0259         Ifaces::Device *iface = createBackendObject(udi);
0260 
0261         DevicePrivate *devData = new DevicePrivate(udi);
0262         devData->setBackendObject(iface);
0263 
0264         QPointer<DevicePrivate> ptr(devData);
0265         m_devicesMap[udi] = ptr;
0266         m_reverseMap[devData] = udi;
0267 
0268         connect(devData, SIGNAL(destroyed(QObject *)), this, SLOT(_k_destroyed(QObject *)));
0269 
0270         return devData;
0271     }
0272 }
0273 
0274 Solid::Ifaces::Device *Solid::DeviceManagerPrivate::createBackendObject(const QString &udi)
0275 {
0276     const QList<QObject *> backends = globalDeviceStorage->managerBackends();
0277 
0278     for (QObject *backendObj : backends) {
0279         Ifaces::DeviceManager *backend = qobject_cast<Ifaces::DeviceManager *>(backendObj);
0280 
0281         if (backend == nullptr) {
0282             continue;
0283         }
0284         if (!udi.startsWith(backend->udiPrefix())) {
0285             continue;
0286         }
0287 
0288         Ifaces::Device *iface = nullptr;
0289 
0290         QObject *object = backend->createDevice(udi);
0291         iface = qobject_cast<Ifaces::Device *>(object);
0292 
0293         if (iface == nullptr) {
0294             delete object;
0295         }
0296 
0297         return iface;
0298     }
0299 
0300     return nullptr;
0301 }
0302 
0303 Solid::DeviceManagerStorage::DeviceManagerStorage()
0304 {
0305 }
0306 
0307 QList<QObject *> Solid::DeviceManagerStorage::managerBackends()
0308 {
0309     ensureManagerCreated();
0310     return m_storage.localData()->managerBackends();
0311 }
0312 
0313 Solid::DeviceNotifier *Solid::DeviceManagerStorage::notifier()
0314 {
0315     ensureManagerCreated();
0316     return m_storage.localData();
0317 }
0318 
0319 void Solid::DeviceManagerStorage::ensureManagerCreated()
0320 {
0321     if (!m_storage.hasLocalData()) {
0322         m_storage.setLocalData(new DeviceManagerPrivate());
0323     }
0324 }
0325 
0326 #include "moc_devicemanager_p.cpp"
0327 #include "moc_devicenotifier.cpp"