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{
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>()) {
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>()) {
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:
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:
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:
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 cout << tr("Device Check: %1").arg(device.as<Solid::StorageAccess>()->check() == 0 ? tr("has error") : tr("no error")) << endl;
482 } else {
483 cout << tr("Device Check: operation is not supported") << endl;
484 }
485 cout << "udi = '" << udi << "'" << endl;
486 return true;
487 case CanRepair:
488 cout << tr("Device CanRepair: %1").arg(device.as<Solid::StorageAccess>()->canRepair() == 0 ? tr("no") : tr("yes")) << endl;
489 cout << "udi = '" << udi << "'" << endl;
490 return true;
491 case Repair:
493 SIGNAL(repairDone(Solid::ErrorType, QVariant, QString)),
494 this,
495 SLOT(slotStorageResult(Solid::ErrorType, QVariant)));
496 device.as<Solid::StorageAccess>()->repair();
497 break;
498 }
499
500 m_loop.exec();
501
502 if (m_error) {
503 cerr << tr("Error: %1").arg(m_errorString) << endl;
504 return false;
505 }
506
507 return true;
508}
509
510bool SolidHardware::listen()
511{
512 Solid::DeviceNotifier *notifier = Solid::DeviceNotifier::instance();
513 bool a = connect(notifier, SIGNAL(deviceAdded(QString)), this, SLOT(deviceAdded(QString)));
514 bool d = connect(notifier, SIGNAL(deviceRemoved(QString)), this, SLOT(deviceRemoved(QString)));
515
516 if (!a || !d) {
517 return false;
518 }
519
520 cout << "Listening to add/remove events: " << endl;
521 m_loop.exec();
522 return true;
523}
524
525bool SolidHardware::monitor(const QString &udi)
526{
527 Solid::Device device(udi);
528
529 if (!device.is<Solid::GenericInterface>())
530 return false;
531
532 auto genericInterface = device.as<Solid::GenericInterface>();
533
534 cout << "udi = '" << device.udi() << "'" << endl;
535 cout << genericInterface->allProperties();
536
538 this, [genericInterface](const auto &changes) {
539 cout << endl;
540 for (auto it = changes.begin(); it != changes.end(); ++it) {
541 cout << " " << it.key() << " = " << genericInterface->property(it.key()) << endl;
542 }
543 });
544
545 m_loop.exec();
546 return true;
547}
548
549void SolidHardware::deviceAdded(const QString &udi)
550{
551 cout << "Device Added:" << endl;
552 cout << "udi = '" << udi << "'" << endl;
553}
554
555void SolidHardware::deviceRemoved(const QString &udi)
556{
557 cout << "Device Removed:" << endl;
558 cout << "udi = '" << udi << "'" << endl;
559}
560
561void SolidHardware::slotStorageResult(Solid::ErrorType error, const QVariant &errorData)
562{
563 if (error) {
564 m_error = 1;
565 m_errorString = errorData.toString();
566 }
567 m_loop.exit();
568}
569
570#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 allow to query the underlying system to obtain information about the hardware available.
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...
Generic interface to deal with a device.
void propertyChanged(const QMap< QString, int > &changes)
This signal is emitted when a property is changed in the device.
This device interface is available on CD-R*,DVD*,Blu-Ray,HD-DVD drives.
This device interface is available on volume devices to access them (i.e.
bool canRepair() const
Indicates if the filesystem of this volume supports repair attempts.
bool check()
Checks the filesystem for consistency avoiding any modifications or repairs.
bool canCheck() const
Indicates if this volume can check for filesystem errors.
Type type(const QSqlDatabase &db)
std::optional< QSqlQuery > query(const QString &queryStatement)
QDebug operator<<(QDebug dbg, const DcrawInfoContainer &c)
QString path(const QString &relativePath)
KIOCORE_EXPORT QStringList list(const QString &fileClass)
KGuiItem properties()
const QList< QKeySequence > & end()
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
int exec(ProcessEventsFlags flags)
void exit(int returnCode)
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-2024 The KDE developers.
Generated on Sat Dec 21 2024 17:03:23 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.