Solid

solid-hardware.cpp
1/* This file is part of the KDE project
2 SPDX-FileCopyrightText: 2006 Kevin Ottens <ervin@kde.org>
3 SPDX-FileCopyrightText: 2014 Alejandro Fiestas Olivares <afiestas@kde.org>
4
5 SPDX-License-Identifier: LGPL-2.1-or-later
6*/
7
8#include "solid-hardware.h"
9
10#ifdef HAVE_DBUS
11#include <QDBusArgument>
12#include <QDBusObjectPath>
13#endif
14#include <QMetaEnum>
15#include <QMetaProperty>
16#include <QString>
17#include <QStringList>
18#include <QTextStream>
19
20#include <QCommandLineParser>
21
22#include <solid/device.h>
23#include <solid/genericinterface.h>
24#include <solid/opticaldrive.h>
25
26#include <iostream>
27#include <solid/devicenotifier.h>
28using namespace std;
29
30static const QString appName{QStringLiteral("solid-hardware")};
31
32static const QString version{QStringLiteral("0.1a")};
33
34std::ostream &operator<<(std::ostream &out, const QString &msg)
35{
36 return (out << msg.toLocal8Bit().constData());
37}
38
39std::ostream &operator<<(std::ostream &out, const QVariant &value);
40#ifdef HAVE_DBUS
41std::ostream &operator<<(std::ostream &out, const QDBusArgument &arg)
42{
43 auto type = arg.currentType();
44 switch (type) {
46 out << " { ";
47 arg.beginArray();
48 while (!arg.atEnd()) {
49 out << arg;
50 if (!arg.atEnd()) {
51 out << ", ";
52 }
53 }
54 arg.endArray();
55 out << " }";
56 break;
58 out << " ( ";
59 arg.beginStructure();
60 while (!arg.atEnd()) {
61 out << arg.asVariant();
62 }
63 arg.endStructure();
64 out << " )";
65 break;
67 out << " [ ";
68 arg.beginMap();
69 if (!arg.atEnd()) {
70 out << arg;
71 }
72 arg.endMap();
73 out << " ]";
74 break;
76 arg.beginMapEntry();
77 out << arg.asVariant() << " = " << arg;
78 arg.endMapEntry();
79 break;
81 out << "(unknown DBus type)";
82 break;
85 out << arg.asVariant();
86 }
87 return out;
88}
89#endif
90
91std::ostream &operator<<(std::ostream &out, const QVariant &value)
92{
93 switch (value.userType()) {
95 out << "{";
96
97 const QStringList list = value.toStringList();
98
101
102 for (; it != end; ++it) {
103 out << "'" << *it << "'";
104
105 if (it + 1 != end) {
106 out << ", ";
107 }
108 }
109
110 out << "} (string list)";
111 break;
112 }
114 out << "'" << value.toString() << "' (string)";
115 break;
116 case QMetaType::Bool:
117 out << (value.toBool() ? "true" : "false") << " (bool)";
118 break;
119 case QMetaType::Int:
121 out << value.toString() << " (0x" << QString::number(value.toLongLong(), 16) << ") (" << value.typeName() << ")";
122 break;
123 case QMetaType::UInt:
125 out << value.toString() << " (0x" << QString::number(value.toULongLong(), 16) << ") (" << value.typeName() << ")";
126 break;
128 out << value.toString() << " (double)";
129 break;
131 out << "'" << value.toString() << "' (bytes)";
132 break;
133 case QMetaType::User:
134 // qDebug() << "got variant type:" << value.typeName();
135 if (value.canConvert<QList<int>>()) {
136 const QList<int> intlist = value.value<QList<int>>();
137 QStringList tmp;
138 for (const int val : intlist) {
139 tmp.append(QString::number(val));
140 }
141 out << "{" << tmp.join(QStringLiteral(",")) << "} (int list)";
142#ifdef HAVE_DBUS
143 } else if (value.canConvert<QDBusObjectPath>()) {
144 out << value.value<QDBusObjectPath>().path() << " (ObjectPath)";
145 } else if (value.canConvert<QDBusVariant>()) {
146 out << value.value<QDBusVariant>().variant() << "(Variant)";
147 } else if (value.canConvert<QDBusArgument>()) {
148 out << value.value<QDBusArgument>();
149#endif
150 } else {
151 out << value.toString() << " (unhandled)";
152 }
153
154 break;
155 default:
156 out << "'" << value.toString() << "' (" << value.typeName() << ")";
157 break;
158 }
159
160 return out;
161}
162
163std::ostream &operator<<(std::ostream &out, const Solid::Device &device)
164{
165 out << " parent = " << QVariant(device.parentUdi()) << endl;
166 out << " vendor = " << QVariant(device.vendor()) << endl;
167 out << " product = " << QVariant(device.product()) << endl;
168 out << " description = " << QVariant(device.description()) << endl;
169 out << " icon = " << QVariant(device.icon()) << endl;
170
171 int index = Solid::DeviceInterface::staticMetaObject.indexOfEnumerator("Type");
172 QMetaEnum typeEnum = Solid::DeviceInterface::staticMetaObject.enumerator(index);
173
174 for (int i = 0; i < typeEnum.keyCount(); i++) {
176 const Solid::DeviceInterface *interface = device.asDeviceInterface(type);
177
178 if (interface) {
179 const QMetaObject *meta = interface->metaObject();
180
181 for (int i = meta->propertyOffset(); i < meta->propertyCount(); i++) {
182 QMetaProperty property = meta->property(i);
183 out << " " << QByteArray(meta->className()).mid(7).constData() << "." << property.name() << " = ";
184
185 QVariant value = property.read(interface);
186
187 if (property.isEnumType()) {
188 QMetaEnum metaEnum = property.enumerator();
189 if (metaEnum.isFlag()) {
190 out << "'" << metaEnum.valueToKeys(value.toInt()).constData() << "'"
191 << " (0x" << QString::number(value.toInt(), 16) << ") (flag)";
192 } else {
193 out << "'" << metaEnum.valueToKey(value.toInt()) << "'"
194 << " (0x" << QString::number(value.toInt(), 16) << ") (enum)";
195 }
196 out << endl;
197 } else {
198 out << value << endl;
199 }
200 }
201 }
202 }
203
204 return out;
205}
206
207std::ostream &operator<<(std::ostream &out, const QMap<QString, QVariant> &properties)
208{
209 for (auto it = properties.cbegin(); it != properties.cend(); ++it) {
210 out << " " << it.key() << " = " << it.value() << endl;
211 }
212
213 return out;
214}
215
216QString getUdiFromArguments(QCoreApplication &app, QCommandLineParser &parser)
217{
218 parser.addPositionalArgument(QStringLiteral("udi"), QCoreApplication::translate("solid-hardware", "Device udi"));
219 parser.process(app);
220 if (parser.positionalArguments().count() < 2) {
221 parser.showHelp(1);
222 }
223 return parser.positionalArguments().at(1);
224}
225
226static QString commandsHelp()
227{
228 QString data;
229 QTextStream cout(&data);
230 cout << '\n' << QCoreApplication::translate("solid-hardware", "Syntax:") << '\n' << '\n';
231
232 cout << " solid-hardware list [details|nonportableinfo]" << '\n';
233 cout << QCoreApplication::translate("solid-hardware",
234 " # List the hardware available in the system.\n"
235 " # - If the 'nonportableinfo' option is specified, the device\n"
236 " # properties are listed (be careful, in this case property names\n"
237 " # are backend dependent),\n"
238 " # - If the 'details' option is specified, the device interfaces\n"
239 " # and the corresponding properties are listed in a platform\n"
240 " # neutral fashion,\n"
241 " # - Otherwise only device UDIs are listed.\n")
242 << '\n';
243
244 cout << " solid-hardware details 'udi'" << '\n';
245 cout << QCoreApplication::translate("solid-hardware",
246 " # Display all the interfaces and properties of the device\n"
247 " # corresponding to 'udi' in a platform neutral fashion.\n")
248 << '\n';
249
250 cout << " solid-hardware nonportableinfo 'udi'" << '\n';
251 cout << QCoreApplication::translate("solid-hardware",
252 " # Display all the properties of the device corresponding to 'udi'\n"
253 " # (be careful, in this case property names are backend dependent).\n")
254 << '\n';
255
256 cout << " solid-hardware query 'predicate' ['parentUdi']" << '\n';
257 cout << QCoreApplication::translate("solid-hardware",
258 " # List the UDI of devices corresponding to 'predicate'.\n"
259 " # - If 'parentUdi' is specified, the search is restricted to the\n"
260 " # branch of the corresponding device,\n"
261 " # - Otherwise the search is done on all the devices.\n")
262 << '\n';
263
264 cout << " solid-hardware mount 'udi'" << '\n';
265 cout << QCoreApplication::translate("solid-hardware", " # If applicable, mount the device corresponding to 'udi'.\n") << '\n';
266
267 cout << " solid-hardware unmount 'udi'" << '\n';
268 cout << QCoreApplication::translate("solid-hardware", " # If applicable, unmount the device corresponding to 'udi'.\n") << '\n';
269
270 cout << " solid-hardware eject 'udi'" << '\n';
271 cout << QCoreApplication::translate("solid-hardware", " # If applicable, eject the device corresponding to 'udi'.\n") << '\n';
272
273 cout << " solid-hardware listen" << '\n';
274 cout << QCoreApplication::translate("solid-hardware", " # Listen to all add/remove events on supported hardware.\n") << '\n';
275
276 cout << " solid-hardware monitor 'udi'" << '\n';
277 cout << QCoreApplication::translate("solid-hardware", " # Monitor devices for changes.\n") << '\n';
278
279 cout << " solid-hardware CanCheck 'udi'" << '\n';
280 cout << QCoreApplication::translate("solid-hardware", " # Send \"CanCheck\" request to the device corresponding to 'udi'.\n") << '\n';
281
282 cout << " solid-hardware Check 'udi'" << '\n';
283 cout << QCoreApplication::translate("solid-hardware", " # Send \"Check\" request to the device corresponding to 'udi'.\n") << '\n';
284
285 cout << " solid-hardware CanRepair 'udi'" << '\n';
286 cout << QCoreApplication::translate("solid-hardware", " # Send \"CanRepair\" request to the device corresponding to 'udi'.\n") << '\n';
287
288 cout << " solid-hardware Repair 'udi'" << '\n';
289 cout << QCoreApplication::translate("solid-hardware", " # Send \"Repair\" request to the device corresponding to 'udi'.\n");
290
291 return data;
292}
293
294int main(int argc, char **argv)
295{
296 SolidHardware app(argc, argv);
297 app.setApplicationName(appName);
298 app.setApplicationVersion(version);
299
300 QCommandLineParser parser;
301 parser.setApplicationDescription(QCoreApplication::translate("solid-hardware", "KDE tool for querying your hardware from the command line"));
302 parser.addHelpOption();
303 parser.addVersionOption();
304 parser.addPositionalArgument(QStringLiteral("command"), QCoreApplication::translate("solid-hardware", "Command to execute"), commandsHelp());
305
306 QCommandLineOption commands(QStringLiteral("commands"), QCoreApplication::translate("solid-hardware", "Show available commands"));
307 // --commands only for backwards compat, it's now in the "syntax help"
308 // of the positional argument.
309 commands.setFlags(QCommandLineOption::HiddenFromHelp);
310 parser.addOption(commands);
311
312 parser.process(app);
313 if (parser.isSet(commands)) {
314 cout << commandsHelp() << endl;
315 return 0;
316 }
317
318 QStringList args = parser.positionalArguments();
319 if (args.count() < 1) {
320 parser.showHelp(1);
321 }
322
324
325 QString command(args.at(0));
326
327 if (command == QLatin1String("list")) {
328 parser.addPositionalArgument(QStringLiteral("details"), QCoreApplication::translate("solid-hardware", "Show device details"));
329 parser.addPositionalArgument(QStringLiteral("nonportableinfo"), QCoreApplication::translate("solid-hardware", "Show non portable information"));
330 parser.process(app);
331 args = parser.positionalArguments();
332 QByteArray extra(args.count() == 2 ? args.at(1).toLocal8Bit() : QByteArray());
333 return app.hwList(extra == "details", extra == "nonportableinfo");
334 } else if (command == QLatin1String("details")) {
335 const QString udi = getUdiFromArguments(app, parser);
336 return app.hwCapabilities(udi);
337 } else if (command == QLatin1String("nonportableinfo")) {
338 const QString udi = getUdiFromArguments(app, parser);
339 return app.hwProperties(udi);
340 } else if (command == QLatin1String("query")) {
341 parser.addPositionalArgument(QStringLiteral("udi"), QCoreApplication::translate("solid-hardware", "Device udi"));
342 parser.addPositionalArgument(QStringLiteral("parent"), QCoreApplication::translate("solid-hardware", "Parent device udi"));
343 parser.process(app);
344 if (parser.positionalArguments().count() < 2 || parser.positionalArguments().count() > 3) {
345 parser.showHelp(1);
346 }
347
348 QString query = args.at(1);
349 QString parent;
350
351 if (args.count() == 3) {
352 parent = args.at(2);
353 }
354
355 return app.hwQuery(parent, query);
356 } else if (command == QLatin1String("mount")) {
357 const QString udi = getUdiFromArguments(app, parser);
358 return app.hwVolumeCall(SolidHardware::Mount, udi);
359 } else if (command == QLatin1String("unmount")) {
360 const QString udi = getUdiFromArguments(app, parser);
361 return app.hwVolumeCall(SolidHardware::Unmount, udi);
362 } else if (command == QLatin1String("eject")) {
363 const QString udi = getUdiFromArguments(app, parser);
364 return app.hwVolumeCall(SolidHardware::Eject, udi);
365 } else if (command == QLatin1String("listen")) {
366 return app.listen();
367 } else if (command == QLatin1String("monitor")) {
368 const QString udi = getUdiFromArguments(app, parser);
369 return app.monitor(udi);
370 } else if (command == QLatin1String("CanCheck")) {
371 const QString udi = getUdiFromArguments(app, parser);
372 return app.hwVolumeCall(SolidHardware::CanCheck, udi);
373 } else if (command == QLatin1String("Check")) {
374 const QString udi = getUdiFromArguments(app, parser);
375 return app.hwVolumeCall(SolidHardware::Check, udi);
376 } else if (command == QLatin1String("CanRepair")) {
377 const QString udi = getUdiFromArguments(app, parser);
378 return app.hwVolumeCall(SolidHardware::CanRepair, udi);
379 } else if (command == QLatin1String("Repair")) {
380 const QString udi = getUdiFromArguments(app, parser);
381 return app.hwVolumeCall(SolidHardware::Repair, udi);
382 }
383
384 cerr << QCoreApplication::translate("solid-hardware", "Syntax Error: Unknown command '%1'").arg(command) << endl;
385
386 return 1;
387}
388
389bool SolidHardware::hwList(bool interfaces, bool system)
390{
391 const QList<Solid::Device> all = Solid::Device::allDevices();
392
393 for (const Solid::Device &device : all) {
394 cout << "udi = '" << device.udi() << "'" << endl;
395
396 if (interfaces) {
397 cout << device << endl;
398 } else if (system && device.is<Solid::GenericInterface>()) {
399 QMap<QString, QVariant> properties = device.as<Solid::GenericInterface>()->allProperties();
400 cout << properties << endl;
401 }
402 }
403
404 return true;
405}
406
407bool SolidHardware::hwCapabilities(const QString &udi)
408{
409 const Solid::Device device(udi);
410
411 cout << "udi = '" << device.udi() << "'" << endl;
412 cout << device << endl;
413
414 return true;
415}
416
417bool SolidHardware::hwProperties(const QString &udi)
418{
419 const Solid::Device device(udi);
420
421 cout << "udi = '" << device.udi() << "'" << endl;
422 if (device.is<Solid::GenericInterface>()) {
423 QMap<QString, QVariant> properties = device.as<Solid::GenericInterface>()->allProperties();
424 cout << properties << endl;
425 }
426
427 return true;
428}
429
430bool SolidHardware::hwQuery(const QString &parentUdi, const QString &query)
431{
432 const QList<Solid::Device> devices = Solid::Device::listFromQuery(query, parentUdi);
433
434 for (const Solid::Device &device : devices) {
435 cout << "udi = '" << device.udi() << "'" << endl;
436 }
437
438 return true;
439}
440
441bool SolidHardware::hwVolumeCall(SolidHardware::VolumeCallType type, const QString &udi)
442{
443 Solid::Device device(udi);
444
445 if (!device.is<Solid::StorageAccess>() && type != Eject) {
446 cerr << tr("Error: %1 does not have the interface StorageAccess.").arg(udi) << endl;
447 return false;
448 } else if (!device.is<Solid::OpticalDrive>() && type == Eject) {
449 cerr << tr("Error: %1 does not have the interface OpticalDrive.").arg(udi) << endl;
450 return false;
451 }
452
453 switch (type) {
454 case Mount:
455 connect(device.as<Solid::StorageAccess>(),
456 SIGNAL(setupDone(Solid::ErrorType, QVariant, QString)),
457 this,
458 SLOT(slotStorageResult(Solid::ErrorType, QVariant)));
459 device.as<Solid::StorageAccess>()->setup();
460 break;
461 case Unmount:
462 connect(device.as<Solid::StorageAccess>(),
463 SIGNAL(teardownDone(Solid::ErrorType, QVariant, QString)),
464 this,
465 SLOT(slotStorageResult(Solid::ErrorType, QVariant)));
466 device.as<Solid::StorageAccess>()->teardown();
467 break;
468 case Eject:
469 connect(device.as<Solid::OpticalDrive>(),
470 SIGNAL(ejectDone(Solid::ErrorType, QVariant, QString)),
471 this,
472 SLOT(slotStorageResult(Solid::ErrorType, QVariant)));
473 device.as<Solid::OpticalDrive>()->eject();
474 break;
475 case CanCheck:
476 cout << tr("Device CanCheck: %1").arg(device.as<Solid::StorageAccess>()->canCheck() == 0 ? tr("no") : tr("yes")) << endl;
477 cout << "udi = '" << udi << "'" << endl;
478 return true;
479 case Check:
480 if (device.as<Solid::StorageAccess>()->canCheck()) {
481 connect(device.as<Solid::StorageAccess>(),
482 SIGNAL(checkDone(Solid::ErrorType, QVariant, QString)),
483 this,
484 SLOT(slotStorageCheckResult(Solid::ErrorType, QVariant, QString)));
485 device.as<Solid::StorageAccess>()->check();
486 } else {
487 cout << tr("Device Check: operation is not supported") << endl;
488 }
489 break;
490 case CanRepair:
491 cout << tr("Device CanRepair: %1").arg(device.as<Solid::StorageAccess>()->canRepair() == 0 ? tr("no") : tr("yes")) << endl;
492 cout << "udi = '" << udi << "'" << endl;
493 return true;
494 case Repair:
495 connect(device.as<Solid::StorageAccess>(),
496 SIGNAL(repairDone(Solid::ErrorType, QVariant, QString)),
497 this,
498 SLOT(slotStorageResult(Solid::ErrorType, QVariant)));
499 device.as<Solid::StorageAccess>()->repair();
500 break;
501 }
502
503 m_loop.exec();
504
505 if (m_error) {
506 cerr << tr("Error: %1").arg(m_errorString) << endl;
507 return false;
508 }
509
510 return true;
511}
512
513bool SolidHardware::listen()
514{
515 Solid::DeviceNotifier *notifier = Solid::DeviceNotifier::instance();
516 bool a = connect(notifier, SIGNAL(deviceAdded(QString)), this, SLOT(deviceAdded(QString)));
517 bool d = connect(notifier, SIGNAL(deviceRemoved(QString)), this, SLOT(deviceRemoved(QString)));
518
519 if (!a || !d) {
520 return false;
521 }
522
523 cout << "Listening to add/remove events: " << endl;
524 m_loop.exec();
525 return true;
526}
527
528bool SolidHardware::monitor(const QString &udi)
529{
530 Solid::Device device(udi);
531
532 if (!device.is<Solid::GenericInterface>())
533 return false;
534
535 auto genericInterface = device.as<Solid::GenericInterface>();
536
537 cout << "udi = '" << device.udi() << "'" << endl;
538 cout << genericInterface->allProperties();
539
541 this, [genericInterface](const auto &changes) {
542 cout << endl;
543 for (auto it = changes.begin(); it != changes.end(); ++it) {
544 cout << " " << it.key() << " = " << genericInterface->property(it.key()) << endl;
545 }
546 });
547
548 m_loop.exec();
549 return true;
550}
551
552void SolidHardware::deviceAdded(const QString &udi)
553{
554 cout << "Device Added:" << endl;
555 cout << "udi = '" << udi << "'" << endl;
556}
557
558void SolidHardware::deviceRemoved(const QString &udi)
559{
560 cout << "Device Removed:" << endl;
561 cout << "udi = '" << udi << "'" << endl;
562}
563
564void SolidHardware::slotStorageResult(Solid::ErrorType error, const QVariant &errorData)
565{
566 if (error) {
567 m_error = 1;
568 m_errorString = errorData.toString();
569 }
570 m_loop.exit();
571}
572
573void SolidHardware::slotStorageCheckResult(Solid::ErrorType error, const QVariant &errorData, const QString &udi)
574{
575 slotStorageResult(error, errorData);
576 if (error == Solid::NoError) {
577 cout << tr("Device check: %1").arg(errorData.toBool() ? tr("no error") : tr("has error")) << endl;
578 cout << "udi = '" << udi << "'" << endl;
579 }
580}
581
582#include "moc_solid-hardware.cpp"
Base class of all the device interfaces.
Type
This enum type defines the type of device interface that a Device can have.
This class allows applications to deal with devices available in the underlying system.
QString description() const
Retrieves the description of device.
QString udi() const
Retrieves the Universal Device Identifier (UDI).
QString parentUdi() const
Retrieves the Universal Device Identifier (UDI) of the Device's parent.
QString icon() const
Retrieves the name of the icon representing this device.
static QList< Device > allDevices()
Retrieves all the devices available in the underlying system.
QString vendor() const
Retrieves the name of the device vendor.
bool is() const
Tests if a device provides a given device interface.
static QList< Device > listFromQuery(const Predicate &predicate, const QString &parentUdi=QString())
Retrieves a list of devices of the system given matching the given constraints (parent and predicate)
QString product() const
Retrieves the name of the product corresponding to this device.
DevIface * as()
Retrieves a specialized interface to interact with the device corresponding to a given device interfa...
void propertyChanged(const QMap< QString, int > &changes)
This signal is emitted when a property is changed in the device.
Type type(const QSqlDatabase &db)
KSERVICE_EXPORT KService::List query(FilterFunc filterFunc)
QAction * end(const QObject *recvr, const char *slot, QObject *parent)
QString path(const QString &relativePath)
KIOCORE_EXPORT QStringList list(const QString &fileClass)
KGuiItem properties()
PHONON_EXPORT Notifier * notifier()
const char * constData() const const
QByteArray mid(qsizetype pos, qsizetype len) const const
QCommandLineOption addHelpOption()
bool addOption(const QCommandLineOption &option)
void addPositionalArgument(const QString &name, const QString &description, const QString &syntax)
QCommandLineOption addVersionOption()
void clearPositionalArguments()
bool isSet(const QCommandLineOption &option) const const
QStringList positionalArguments() const const
void process(const QCoreApplication &app)
void setApplicationDescription(const QString &description)
void showHelp(int exitCode)
void setApplicationName(const QString &application)
void setApplicationVersion(const QString &version)
QString translate(const char *context, const char *sourceText, const char *disambiguation, int n)
bool atEnd() const const
void beginArray(QMetaType id)
ElementType currentType() const const
typedef ConstIterator
void append(QList< T > &&value)
const_reference at(qsizetype i) const const
const_iterator constBegin() const const
const_iterator constEnd() const const
qsizetype count() const const
bool isFlag() const const
int keyCount() const const
int value(int index) const const
const char * valueToKey(int value) const const
QByteArray valueToKeys(int value) const const
const char * className() const const
QMetaProperty property(int index) const const
int propertyOffset() const const
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
QString tr(const char *sourceText, const char *disambiguation, int n)
QString arg(Args &&... args) const const
QString number(double n, char format, int precision)
QByteArray toLocal8Bit() const const
QTextStream & endl(QTextStream &stream)
bool canConvert() const const
bool toBool() const const
int toInt(bool *ok) const const
qlonglong toLongLong(bool *ok) const const
QString toString() const const
QStringList toStringList() const const
qulonglong toULongLong(bool *ok) const const
const char * typeName() const const
int userType() const const
T value() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2025 The KDE developers.
Generated on Fri Feb 21 2025 11:55:36 by doxygen 1.13.2 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.