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

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