Solid

iokitdevice.cpp
1/*
2 SPDX-FileCopyrightText: 2009 Harald Fernengel <harry@kdevelop.org>
3 SPDX-FileCopyrightText: 2017 René J.V. Bertin <rjvbertin@gmail.com>
4
5 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
6*/
7
8#include "iokitdevice.h"
9#include "iokitbattery.h"
10#include "iokitgenericinterface.h"
11#include "iokitopticaldisc.h"
12#include "iokitopticaldrive.h"
13#include "iokitprocessor.h"
14#include "iokitstorage.h"
15#include "iokitstorageaccess.h"
16#include "iokitvolume.h"
17
18#include <QDebug>
19#include <QSet>
20#include <QUrl>
21
22#include <sys/sysctl.h>
23#include <sys/types.h>
24
25#include <IOKit/network/IOEthernetInterface.h>
26#include <IOKit/usb/IOUSBLib.h>
27
28#include <CoreFoundation/CoreFoundation.h>
29
30// from cfhelper.cpp
31extern QMap<QString, QVariant> q_toVariantMap(const CFMutableDictionaryRef &dict);
32extern bool q_sysctlbyname(const char *name, QString &result);
33
35
36namespace Solid
37{
38namespace Backends
39{
40namespace IOKit
41{
42// returns a solid type from an entry and its properties
43static DeviceInterfaceTypes typesFromEntry(const io_registry_entry_t &entry, const QMap<QString, QVariant> &properties, Solid::DeviceInterface::Type &mainType)
44{
46 mainType = Solid::DeviceInterface::Unknown;
47 if (IOObjectConformsTo(entry, "AppleACPICPU")) {
48 mainType = Solid::DeviceInterface::Processor;
49 types << mainType;
50 }
51 if (IOObjectConformsTo(entry, "AppleSmartBattery")) {
52 mainType = Solid::DeviceInterface::Battery;
53 types << mainType;
54 }
55 const QString bsdName = QStringLiteral("BSD Name");
56 const QString leaf = QStringLiteral("Leaf");
57 if (IOObjectConformsTo(entry, "IOCDMedia") //
58 || IOObjectConformsTo(entry, "IODVDMedia") //
59 || IOObjectConformsTo(entry, "IOBDMedia")) {
60 mainType = Solid::DeviceInterface::OpticalDrive;
61 types << mainType << Solid::DeviceInterface::OpticalDisc;
62 }
63 if (properties.contains(bsdName) && properties.value(bsdName).toString().startsWith(QStringLiteral("disk"))) {
64 if ((properties.contains(leaf) && properties.value(leaf).toBool() == false) //
65 || mainType == Solid::DeviceInterface::OpticalDrive) {
66 if (mainType == Solid::DeviceInterface::Unknown) {
67 mainType = Solid::DeviceInterface::StorageDrive;
68 }
69 types << Solid::DeviceInterface::StorageDrive;
70 } else if (mainType == Solid::DeviceInterface::Unknown) {
71 mainType = Solid::DeviceInterface::StorageVolume;
72 }
73 types << Solid::DeviceInterface::StorageVolume;
74 }
75
76 if (types.isEmpty()) {
77 types << mainType;
78 // qWarning() << "unsupported entry" << entry << "with properties" << properties;
79 }
80
81 return types;
82}
83
84// gets all properties from an entry into a QMap
85static QMap<QString, QVariant> getProperties(const io_registry_entry_t &entry)
86{
87 CFMutableDictionaryRef propertyDict = 0;
88
89 if (IORegistryEntryCreateCFProperties(entry, &propertyDict, kCFAllocatorDefault, kNilOptions) != KERN_SUCCESS) {
91 }
92
93 QMap<QString, QVariant> result = q_toVariantMap(propertyDict);
94
95 CFRelease(propertyDict);
96
97 io_name_t className;
98 IOObjectGetClass(entry, className);
99 result[QStringLiteral("className")] = QString::fromUtf8(className);
100
101 return result;
102}
103
104// gets the parent's Udi from an entry
105static QString getParentDeviceUdi(const io_registry_entry_t &entry)
106{
107 io_registry_entry_t parent = 0;
108 kern_return_t ret = IORegistryEntryGetParentEntry(entry, kIOServicePlane, &parent);
109 if (ret != KERN_SUCCESS) {
110 // don't release parent here - docs say only on success
111 return QString();
112 }
113
114 CFStringRef path = IORegistryEntryCopyPath(parent, kIOServicePlane);
115 QString result = QString::fromCFString(path);
116 CFRelease(path);
117
118 // now we can release the parent
119 IOObjectRelease(parent);
120
121 return result;
122}
123
124static const QString computerModel()
125{
126 QString qModel;
127 q_sysctlbyname("hw.model", qModel);
128 return qModel;
129}
130
131class IOKitDevicePrivate
132{
133public:
134 inline IOKitDevicePrivate()
135 : type({Solid::DeviceInterface::Unknown})
136 , parentDevice(nullptr)
137 {
138 }
139 ~IOKitDevicePrivate()
140 {
141 if (parentDevice) {
142 delete parentDevice;
143 parentDevice = nullptr;
144 }
145 }
146
147 void init(const QString &udiString, const io_registry_entry_t &entry);
148 IOKitDevice *getParentDevice();
149
150 QString udi;
151 QString parentUdi;
155 IOKitDevice *parentDevice;
156};
157
158void IOKitDevicePrivate::init(const QString &udiString, const io_registry_entry_t &entry)
159{
160 Q_ASSERT(entry != MACH_PORT_NULL);
161
162 udi = udiString;
163
164 properties = getProperties(entry);
165
166 parentUdi = getParentDeviceUdi(entry);
167 type = typesFromEntry(entry, properties, mainType);
168 if (udi.contains(QStringLiteral("IOBD")) || udi.contains(QStringLiteral("BD PX"))) {
169 qWarning() << "Solid: BlueRay entry" << entry << "mainType=" << mainType << "typeList:" << type << "with properties" << properties;
170 }
171 if (mainType != Solid::DeviceInterface::Unknown) { }
172
173 IOObjectRelease(entry);
174}
175
176IOKitDevice *IOKitDevicePrivate::getParentDevice()
177{
178 if (!parentDevice) {
179 parentDevice = new IOKitDevice(parentUdi);
180 }
181 return parentDevice;
182}
183
184IOKitDevice::IOKitDevice(const QString &udi, const io_registry_entry_t &entry)
185 : d(new IOKitDevicePrivate)
186{
187 d->init(udi, entry);
188}
189
190IOKitDevice::IOKitDevice(const QString &udi)
191 : d(new IOKitDevicePrivate)
192{
193 if (udi.isEmpty()) {
194 qWarning() << Q_FUNC_INFO << "Tried to create Device from empty UDI";
195 return;
196 }
197
198 CFStringRef path = udi.toCFString();
199 io_registry_entry_t entry = IORegistryEntryCopyFromPath(kIOMasterPortDefault, path);
200 CFRelease(path);
201
202 if (entry == MACH_PORT_NULL) {
203 qWarning() << Q_FUNC_INFO << "Tried to create Device from invalid UDI" << udi;
204 return;
205 }
206
207 d->init(udi, entry);
208}
209
210IOKitDevice::IOKitDevice(const IOKitDevice &device)
211 : d(new IOKitDevicePrivate)
212{
213 if (device.udi().isEmpty()) {
214 qWarning() << Q_FUNC_INFO << "Tried to create Device from empty UDI";
215 return;
216 }
217
218 CFStringRef path = device.udi().toCFString();
219 io_registry_entry_t entry = IORegistryEntryCopyFromPath(kIOMasterPortDefault, path);
220 CFRelease(path);
221
222 if (entry == MACH_PORT_NULL) {
223 qWarning() << Q_FUNC_INFO << "Tried to create Device from invalid UDI" << device.udi();
224 return;
225 }
226
227 d->init(device.udi(), entry);
228}
229
230IOKitDevice::~IOKitDevice()
231{
232 delete d;
233}
234
235bool IOKitDevice::conformsToIOKitClass(const QString &className) const
236{
237 bool conforms = false;
238 if (!className.isEmpty()) {
239 CFStringRef path = udi().toCFString();
240 io_registry_entry_t entry = IORegistryEntryCopyFromPath(kIOMasterPortDefault, path);
241 CFRelease(path);
242 if (entry != MACH_PORT_NULL) {
243 conforms = IOObjectConformsTo(entry, className.toLocal8Bit().constData());
244 IOObjectRelease(entry);
245 }
246 }
247 return conforms;
248}
249
250QString IOKitDevice::udi() const
251{
252 return d->udi;
253}
254
255QString IOKitDevice::parentUdi() const
256{
257 return d->parentUdi;
258}
259
260QString IOKitDevice::vendor() const
261{
262 if (parentUdi().isEmpty()) {
263 return QStringLiteral("Apple");
264 }
265 switch (d->mainType) {
266 case Solid::DeviceInterface::Processor:
267 return Processor::vendor();
268 break;
269 case Solid::DeviceInterface::Battery:
270 return property(QStringLiteral("Manufacturer")).toString();
271 break;
272 case Solid::DeviceInterface::StorageDrive:
273 case Solid::DeviceInterface::OpticalDrive:
274 case Solid::DeviceInterface::OpticalDisc:
275 return IOKitStorage(this).vendor();
276 break;
277 case Solid::DeviceInterface::StorageVolume:
278 return IOKitVolume(this).vendor();
279 break;
280 default:
281 return QString();
282 break;
283 }
284 return QString();
285}
286
287QString IOKitDevice::product() const
288{
289 if (parentUdi().isEmpty()) {
290 return computerModel();
291 }
292 switch (d->mainType) {
293 case Solid::DeviceInterface::Processor:
294 return Processor::product();
295 break;
296 case Solid::DeviceInterface::Battery:
297 return property(QStringLiteral("DeviceName")).toString();
298 break;
299 case Solid::DeviceInterface::StorageDrive:
300 case Solid::DeviceInterface::OpticalDrive:
301 case Solid::DeviceInterface::OpticalDisc:
302 return IOKitStorage(this).product();
303 break;
304 case Solid::DeviceInterface::StorageVolume:
305 return IOKitVolume(this).product();
306 break;
307 }
308 return QString(); // TODO
309}
310
311QString IOKitDevice::description() const
312{
313 switch (d->mainType) {
314 case Solid::DeviceInterface::Processor:
315 return QStringLiteral("Processor");
316 break;
317 case Solid::DeviceInterface::Battery:
318 return QStringLiteral("Apple Smart Battery");
319 break;
320 case Solid::DeviceInterface::StorageDrive:
321 case Solid::DeviceInterface::OpticalDrive:
322 case Solid::DeviceInterface::OpticalDisc:
323 return IOKitStorage(this).description();
324 break;
325 case Solid::DeviceInterface::StorageVolume: {
326 const QString volLabel = IOKitVolume(this).description();
327 const QString mountPoint = IOKitStorageAccess(this).filePath();
328 if (volLabel.isEmpty()) {
329 return QUrl::fromLocalFile(mountPoint).fileName();
330 } else if (mountPoint.startsWith(QStringLiteral("/Volumes/"))) {
331 // Mac users will expect to see the name under which the volume is mounted here.
332 return QString(QStringLiteral("%1 (%2)")).arg(QUrl::fromLocalFile(mountPoint).fileName()).arg(volLabel);
333 }
334 return volLabel;
335 break;
336 }
337 }
338 return product(); // TODO
339}
340
341QString IOKitDevice::icon() const
342{
343 // adapted from HalDevice::icon()
344 if (parentUdi().isEmpty()) {
345 if (computerModel().contains(QStringLiteral("MacBook"))) {
346 return QStringLiteral("computer-laptop");
347 } else {
348 return QStringLiteral("computer");
349 }
350
351 } else if (d->type.contains(Solid::DeviceInterface::StorageDrive)) {
352 IOKitStorage drive(this);
353 Solid::StorageDrive::DriveType driveType = drive.driveType();
354
355 switch (driveType) {
356 case Solid::StorageDrive::Floppy:
357 // why not :)
358 return QStringLiteral("media-floppy");
359 break;
360 case Solid::StorageDrive::CdromDrive: {
361 const IOKitOpticalDisc disc(this);
362 if (disc.availableContent() == Solid::OpticalDisc::Audio) {
363 return QStringLiteral("media-optical-audio");
364 } else
365 switch (disc.discType()) {
366 case Solid::OpticalDisc::CdRom:
367 return QStringLiteral("media-optical-data");
368 break;
369 case Solid::OpticalDisc::CdRecordable:
370 case Solid::OpticalDisc::CdRewritable:
371 return QStringLiteral("media-optical-recordable");
372 break;
373 case Solid::OpticalDisc::BluRayRom:
374 case Solid::OpticalDisc::BluRayRecordable:
375 case Solid::OpticalDisc::BluRayRewritable:
376 return QStringLiteral("media-optical-blu-ray");
377 break;
378 }
379 break;
380 }
381 case Solid::StorageDrive::SdMmc:
382 return QStringLiteral("media-flash-sd-mmc");
383 break;
384 case Solid::StorageDrive::CompactFlash:
385 return QStringLiteral("media-flash-cf");
386 break;
387 }
388 if (drive.bus() == Solid::StorageDrive::Usb) {
389 return QStringLiteral("drive-removable-media-usb");
390 }
391 if (drive.isRemovable()) {
392 return QStringLiteral("drive-removable-media");
393 }
394 return QStringLiteral("drive-harddisk");
395
396 } else if (d->mainType == Solid::DeviceInterface::StorageVolume) {
397 } else if (d->mainType == Solid::DeviceInterface::Battery) {
398 return QStringLiteral("battery");
399 } else if (d->mainType == Solid::DeviceInterface::Processor) {
400 return QStringLiteral("cpu"); // FIXME: Doesn't follow icon spec
401 } else {
402 QString iconName = d->getParentDevice()->icon();
403
404 if (!iconName.isEmpty()) {
405 return iconName;
406 }
407
408 return QStringLiteral("drive-harddisk");
409 }
410 return QString();
411}
412
413QStringList IOKitDevice::emblems() const
414{
415 return QStringList(); // TODO
416}
417
418QVariant IOKitDevice::property(const QString &key) const
419{
420 if (!d->properties.contains(key)) {
421 return QObject::property(key.toUtf8().constData());
422 }
423 return d->properties.value(key);
424}
425
426QMap<QString, QVariant> IOKitDevice::allProperties() const
427{
428 return d->properties;
429}
430
431bool IOKitDevice::iOKitPropertyExists(const QString &key) const
432{
433 return d->properties.contains(key);
434}
435
436bool IOKitDevice::queryDeviceInterface(const Solid::DeviceInterface::Type &type) const
437{
438 switch (type) {
439 case Solid::DeviceInterface::GenericInterface:
440 return true;
441 break;
442 case Solid::DeviceInterface::StorageAccess:
443 if (d->type.contains(Solid::DeviceInterface::StorageDrive) //
444 || d->type.contains(Solid::DeviceInterface::StorageVolume)) {
445 return true;
446 }
447 break;
448 default:
449 return d->type.contains(type);
450 break;
451 }
452 return false;
453}
454
455QObject *IOKitDevice::createDeviceInterface(const Solid::DeviceInterface::Type &type)
456{
457 QObject *iface = nullptr;
458
459 switch (type) {
460 case Solid::DeviceInterface::GenericInterface:
461 iface = new GenericInterface(this);
462 break;
463 case Solid::DeviceInterface::Processor:
464 if (d->type.contains(Solid::DeviceInterface::Processor)) {
465 iface = new Processor(this);
466 }
467 break;
468 case Solid::DeviceInterface::Battery:
469 if (d->type.contains(Solid::DeviceInterface::Battery)) {
470 iface = new Battery(this);
471 }
472 break;
473 case Solid::DeviceInterface::OpticalDrive:
474 if (d->type.contains(Solid::DeviceInterface::OpticalDrive)) {
475 iface = new IOKitOpticalDrive(this);
476 }
477 break;
478 case Solid::DeviceInterface::OpticalDisc:
479 if (d->type.contains(Solid::DeviceInterface::OpticalDisc)) {
480 iface = new IOKitOpticalDisc(this);
481 }
482 break;
483 case Solid::DeviceInterface::StorageDrive:
484 if (d->type.contains(Solid::DeviceInterface::StorageDrive)) {
485 iface = new IOKitStorage(this);
486 }
487 break;
488 case Solid::DeviceInterface::Block:
489 if (d->type.contains(Solid::DeviceInterface::OpticalDisc)) {
490 iface = new IOKitOpticalDisc(this);
491 } else if (d->type.contains(Solid::DeviceInterface::OpticalDrive)) {
492 iface = new IOKitOpticalDrive(this);
493 } else if (d->type.contains(Solid::DeviceInterface::StorageVolume)) {
494 iface = new IOKitVolume(this);
495 } else if (d->type.contains(Solid::DeviceInterface::StorageDrive)) {
496 iface = new IOKitStorage(this);
497 }
498 break;
499 case Solid::DeviceInterface::StorageVolume:
500 if (d->type.contains(Solid::DeviceInterface::StorageVolume)) {
501 iface = new IOKitVolume(this);
502 }
503 break;
504 case Solid::DeviceInterface::StorageAccess:
505 if (d->type.contains(Solid::DeviceInterface::StorageDrive) //
506 || d->type.contains(Solid::DeviceInterface::StorageVolume)) {
507 iface = new IOKitStorageAccess(this);
508 }
509 break;
510 // the rest is TODO
511 }
512
513 return iface;
514}
515
516}
517}
518} // namespaces
519
520#include "moc_iokitdevice.cpp"
Type
This enum type defines the type of device interface that a Device can have.
DriveType
This enum type defines the type of drive a storage device can be.
KDB_EXPORT void getProperties(const KDbLookupFieldSchema *lookup, QMap< QByteArray, QVariant > *values)
QString path(const QString &relativePath)
KGuiItem properties()
const char * constData() const const
bool contains(const Key &key) const const
QVariant property(const char *name) const const
bool isEmpty() const const
QString arg(Args &&... args) const const
bool contains(QChar ch, Qt::CaseSensitivity cs) const const
QString fromCFString(CFStringRef string)
QString fromUtf8(QByteArrayView str)
bool isEmpty() const const
bool startsWith(QChar c, Qt::CaseSensitivity cs) const const
CFStringRef toCFString() const const
QByteArray toLocal8Bit() const const
QByteArray toUtf8() const const
QString fileName(ComponentFormattingOptions options) const const
QUrl fromLocalFile(const QString &localFile)
T value() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri Nov 22 2024 12:01:48 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.