1/*
2 SPDX-FileCopyrightText: 2005-2007 Kevin Ottens <ervin@kde.org>
3
4 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
5*/
6
7#include "devicemanager_p.h" //krazy:exclude=includes (devicenotifier.h is the header file for this class)
8#include "devicenotifier.h"
9
10#include "device.h"
11#include "device_p.h"
12#include "devices_debug.h"
13#include "predicate.h"
14#include "storageaccess.h"
15#include "storagevolume.h"
16
17#include "ifaces/device.h"
18#include "ifaces/devicemanager.h"
19
20#include "soliddefs_p.h"
21
22#include <QLoggingCategory>
23
24#include <set>
25
26Q_GLOBAL_STATIC(Solid::DeviceManagerStorage, globalDeviceStorage)
27
28Solid::DeviceManagerPrivate::DeviceManagerPrivate()
29 : m_nullDevice(new DevicePrivate(QString()))
30{
31 loadBackends();
32
33 const auto backends = managerBackends();
34 for (const auto &backend : backends) {
35 connect(sender: backend, signal: &Solid::Ifaces::DeviceManager::deviceAdded, context: this, slot: &Solid::DeviceManagerPrivate::_k_deviceAdded);
36 connect(sender: backend, signal: &Solid::Ifaces::DeviceManager::deviceRemoved, context: this, slot: &Solid::DeviceManagerPrivate::_k_deviceRemoved);
37 }
38}
39
40Solid::DeviceManagerPrivate::~DeviceManagerPrivate()
41{
42 const auto backends = managerBackends();
43 for (const auto &backend : backends) {
44 disconnect(sender: backend, signal: &Solid::Ifaces::DeviceManager::deviceAdded, receiver: this, slot: &Solid::DeviceManagerPrivate::_k_deviceAdded);
45 disconnect(sender: backend, signal: &Solid::Ifaces::DeviceManager::deviceRemoved, receiver: this, slot: &Solid::DeviceManagerPrivate::_k_deviceRemoved);
46 }
47
48 // take a copy as m_devicesMap is changed by Solid::DeviceManagerPrivate::_k_destroyed
49 const auto deviceMap = m_devicesMap;
50 for (QPointer<DevicePrivate> dev : deviceMap) {
51 if (!dev.data()->ref.deref()) {
52 delete dev.data();
53 }
54 }
55
56 m_devicesMap.clear();
57}
58
59QList<Solid::Device> Solid::Device::allDevices()
60{
61 QList<Device> list;
62 const auto backends = globalDeviceStorage->managerBackends();
63
64 for (const auto &backend : backends) {
65 const auto udis = backend->allDevices();
66 for (const auto &udi : udis) {
67 list.append(t: Device(udi));
68 }
69 }
70
71 return list;
72}
73
74QList<Solid::Device> Solid::Device::listFromQuery(const QString &predicate, const QString &parentUdi)
75{
76 Predicate p = Predicate::fromString(predicate);
77
78 if (p.isValid()) {
79 return listFromQuery(predicate: p, parentUdi);
80 } else {
81 return QList<Device>();
82 }
83}
84
85QList<Solid::Device> Solid::Device::listFromType(const DeviceInterface::Type &type, const QString &parentUdi)
86{
87 QList<Device> list;
88 const auto backends = globalDeviceStorage->managerBackends();
89
90 for (const auto &backend : backends) {
91 if (!backend->supportedInterfaces().contains(value: type)) {
92 continue;
93 }
94
95 const auto udis = backend->devicesFromQuery(parentUdi, type);
96 for (const auto &udi : udis) {
97 list.append(t: Device(udi));
98 }
99 }
100
101 return list;
102}
103
104QList<Solid::Device> Solid::Device::listFromQuery(const Predicate &predicate, const QString &parentUdi)
105{
106 QList<Device> list;
107 const auto usedTypes = predicate.usedTypes();
108 const auto backends = globalDeviceStorage->managerBackends();
109
110 for (const auto &backend : backends) {
111 QStringList udis;
112 if (predicate.isValid()) {
113 auto supportedTypes = backend->supportedInterfaces();
114 if (supportedTypes.intersect(other: usedTypes).isEmpty()) {
115 continue;
116 }
117
118 auto sortedTypes = supportedTypes.values();
119 std::sort(first: sortedTypes.begin(), last: sortedTypes.end());
120 for (const auto &type : std::as_const(t&: sortedTypes)) {
121 udis += backend->devicesFromQuery(parentUdi, type);
122 }
123 } else {
124 udis += backend->allDevices();
125 }
126
127 std::set<QString> seen;
128 for (const auto &udi : std::as_const(t&: udis)) {
129 const auto [it, isInserted] = seen.insert(x: udi);
130 if (!isInserted) {
131 continue;
132 }
133 const Device dev(udi);
134
135 bool matches = false;
136
137 if (!predicate.isValid()) {
138 matches = true;
139 } else {
140 matches = predicate.matches(device: dev);
141 }
142
143 if (matches) {
144 list.append(t: dev);
145 }
146 }
147 }
148
149 return list;
150}
151
152Solid::Device Solid::Device::storageAccessFromPath(const QString &path)
153{
154 const QList<Device> list = Solid::Device::listFromType(type: DeviceInterface::Type::StorageAccess);
155 Device match;
156 int match_length = 0;
157 for (const Device &device : list) {
158 auto storageVolume = device.as<StorageVolume>();
159 if (storageVolume && storageVolume->usage() != StorageVolume::UsageType::FileSystem) {
160 continue;
161 }
162
163 auto storageAccess = device.as<StorageAccess>();
164
165 if (!storageAccess) {
166 continue;
167 }
168
169 QString mountPath = storageAccess->filePath();
170
171 if (mountPath.size() <= match_length || !path.startsWith(s: mountPath)) {
172 continue;
173 }
174
175 const auto realLength = mountPath.back() == QLatin1Char('/') ? mountPath.size() - 1 : mountPath.size();
176
177 // `startsWith` implies `path.size() >= mountPath.size()`
178 if (path.size() == realLength || path[realLength] == QLatin1Char('/')) {
179 match_length = realLength;
180 match = device;
181 }
182 }
183 return match;
184}
185
186Solid::DeviceNotifier *Solid::DeviceNotifier::instance()
187{
188 return globalDeviceStorage->notifier();
189}
190
191void Solid::DeviceManagerPrivate::_k_deviceAdded(const QString &udi)
192{
193 if (m_devicesMap.contains(key: udi)) {
194 DevicePrivate *dev = m_devicesMap[udi].data();
195
196 // Ok, this one was requested somewhere was invalid
197 // and now becomes magically valid!
198
199 if (dev && dev->backendObject() == nullptr) {
200 dev->setBackendObject(createBackendObject(udi));
201 Q_ASSERT(dev->backendObject() != nullptr);
202 }
203 }
204
205 Q_EMIT deviceAdded(udi);
206}
207
208void Solid::DeviceManagerPrivate::_k_deviceRemoved(const QString &udi)
209{
210 if (m_devicesMap.contains(key: udi)) {
211 DevicePrivate *dev = m_devicesMap[udi].data();
212
213 // Ok, this one was requested somewhere was valid
214 // and now becomes magically invalid!
215
216 if (dev) {
217 dev->setBackendObject(nullptr);
218 Q_ASSERT(dev->backendObject() == nullptr);
219 }
220 }
221
222 Q_EMIT deviceRemoved(udi);
223}
224
225void Solid::DeviceManagerPrivate::_k_destroyed(QObject *object)
226{
227 QString udi = m_reverseMap.take(key: object);
228
229 if (!udi.isEmpty()) {
230 m_devicesMap.remove(key: udi);
231 }
232}
233
234Solid::DevicePrivate *Solid::DeviceManagerPrivate::findRegisteredDevice(const QString &udi)
235{
236 if (udi.isEmpty()) {
237 return m_nullDevice.data();
238 } else if (m_devicesMap.contains(key: udi)) {
239 return m_devicesMap[udi].data();
240 } else {
241 Ifaces::Device *iface = createBackendObject(udi);
242
243 DevicePrivate *devData = new DevicePrivate(udi);
244 devData->setBackendObject(iface);
245
246 QPointer<DevicePrivate> ptr(devData);
247 m_devicesMap[udi] = ptr;
248 m_reverseMap[devData] = udi;
249
250 connect(sender: devData, signal: &QObject::destroyed, context: this, slot: &DeviceManagerPrivate::_k_destroyed);
251
252 return devData;
253 }
254}
255
256Solid::Ifaces::Device *Solid::DeviceManagerPrivate::createBackendObject(const QString &udi)
257{
258 const auto backends = globalDeviceStorage->managerBackends();
259
260 for (const auto &backend : backends) {
261 if (!udi.startsWith(s: backend->udiPrefix())) {
262 continue;
263 }
264
265 Ifaces::Device *iface = nullptr;
266
267 QObject *object = backend->createDevice(udi);
268 iface = qobject_cast<Ifaces::Device *>(object);
269
270 if (iface == nullptr) {
271 delete object;
272 }
273
274 return iface;
275 }
276
277 return nullptr;
278}
279
280Solid::DeviceManagerStorage::DeviceManagerStorage()
281{
282}
283
284QList<Solid::Ifaces::DeviceManager *> Solid::DeviceManagerStorage::managerBackends()
285{
286 ensureManagerCreated();
287 return m_storage.localData()->managerBackends();
288}
289
290Solid::DeviceNotifier *Solid::DeviceManagerStorage::notifier()
291{
292 ensureManagerCreated();
293 return m_storage.localData();
294}
295
296void Solid::DeviceManagerStorage::ensureManagerCreated()
297{
298 if (!m_storage.hasLocalData()) {
299 m_storage.setLocalData(new DeviceManagerPrivate());
300 }
301}
302
303#include "moc_devicemanager_p.cpp"
304#include "moc_devicenotifier.cpp"
305

source code of solid/src/solid/devices/frontend/devicemanager.cpp