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#if defined QT_DBUS_LIB
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 char appName[] = "solid-hardware";
31
32static const char version[] = "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#if defined QT_DBUS_LIB
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(",") << "} (int list)";
142#if defined QT_DBUS_LIB
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 << " " << QString(meta->className()).mid(7) << "." << 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("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");
278
279 return data;
280}
281
282int main(int argc, char **argv)
283{
284 SolidHardware app(argc, argv);
285 app.setApplicationName(appName);
286 app.setApplicationVersion(version);
287
288 QCommandLineParser parser;
289 parser.setApplicationDescription(QCoreApplication::translate("solid-hardware", "KDE tool for querying your hardware from the command line"));
290 parser.addHelpOption();
291 parser.addVersionOption();
292 parser.addPositionalArgument("command", QCoreApplication::translate("solid-hardware", "Command to execute"), commandsHelp());
293
294 QCommandLineOption commands("commands", QCoreApplication::translate("solid-hardware", "Show available commands"));
295 // --commands only for backwards compat, it's now in the "syntax help"
296 // of the positional argument.
297 commands.setFlags(QCommandLineOption::HiddenFromHelp);
298 parser.addOption(commands);
299
300 parser.process(app);
301 if (parser.isSet(commands)) {
302 cout << commandsHelp() << endl;
303 return 0;
304 }
305
306 QStringList args = parser.positionalArguments();
307 if (args.count() < 1) {
308 parser.showHelp(1);
309 }
310
312
313 QString command(args.at(0));
314
315 if (command == "list") {
316 parser.addPositionalArgument("details", QCoreApplication::translate("solid-hardware", "Show device details"));
317 parser.addPositionalArgument("nonportableinfo", QCoreApplication::translate("solid-hardware", "Show non portable information"));
318 parser.process(app);
319 args = parser.positionalArguments();
320 QByteArray extra(args.count() == 2 ? args.at(1).toLocal8Bit() : QByteArray());
321 return app.hwList(extra == "details", extra == "nonportableinfo");
322 } else if (command == "details") {
323 const QString udi = getUdiFromArguments(app, parser);
324 return app.hwCapabilities(udi);
325 } else if (command == "nonportableinfo") {
326 const QString udi = getUdiFromArguments(app, parser);
327 return app.hwProperties(udi);
328 } else if (command == "query") {
329 parser.addPositionalArgument("udi", QCoreApplication::translate("solid-hardware", "Device udi"));
330 parser.addPositionalArgument("parent", QCoreApplication::translate("solid-hardware", "Parent device udi"));
331 parser.process(app);
332 if (parser.positionalArguments().count() < 2 || parser.positionalArguments().count() > 3) {
333 parser.showHelp(1);
334 }
335
336 QString query = args.at(1);
337 QString parent;
338
339 if (args.count() == 3) {
340 parent = args.at(2);
341 }
342
343 return app.hwQuery(parent, query);
344 } else if (command == "mount") {
345 const QString udi = getUdiFromArguments(app, parser);
346 return app.hwVolumeCall(SolidHardware::Mount, udi);
347 } else if (command == "unmount") {
348 const QString udi = getUdiFromArguments(app, parser);
349 return app.hwVolumeCall(SolidHardware::Unmount, udi);
350 } else if (command == "eject") {
351 const QString udi = getUdiFromArguments(app, parser);
352 return app.hwVolumeCall(SolidHardware::Eject, udi);
353 } else if (command == "listen") {
354 return app.listen();
355 } else if (command == "monitor") {
356 const QString udi = getUdiFromArguments(app, parser);
357 return app.monitor(udi);
358 }
359
360 cerr << QCoreApplication::translate("solid-hardware", "Syntax Error: Unknown command '%1'").arg(command) << endl;
361
362 return 1;
363}
364
365bool SolidHardware::hwList(bool interfaces, bool system)
366{
368
369 for (const Solid::Device &device : all) {
370 cout << "udi = '" << device.udi() << "'" << endl;
371
372 if (interfaces) {
373 cout << device << endl;
374 } else if (system && device.is<Solid::GenericInterface>()) {
376 cout << properties << endl;
377 }
378 }
379
380 return true;
381}
382
383bool SolidHardware::hwCapabilities(const QString &udi)
384{
385 const Solid::Device device(udi);
386
387 cout << "udi = '" << device.udi() << "'" << endl;
388 cout << device << endl;
389
390 return true;
391}
392
393bool SolidHardware::hwProperties(const QString &udi)
394{
395 const Solid::Device device(udi);
396
397 cout << "udi = '" << device.udi() << "'" << endl;
398 if (device.is<Solid::GenericInterface>()) {
400 cout << properties << endl;
401 }
402
403 return true;
404}
405
406bool SolidHardware::hwQuery(const QString &parentUdi, const QString &query)
407{
408 const QList<Solid::Device> devices = Solid::Device::listFromQuery(query, parentUdi);
409
410 for (const Solid::Device &device : devices) {
411 cout << "udi = '" << device.udi() << "'" << endl;
412 }
413
414 return true;
415}
416
417bool SolidHardware::hwVolumeCall(SolidHardware::VolumeCallType type, const QString &udi)
418{
419 Solid::Device device(udi);
420
421 if (!device.is<Solid::StorageAccess>() && type != Eject) {
422 cerr << tr("Error: %1 does not have the interface StorageAccess.").arg(udi) << endl;
423 return false;
424 } else if (!device.is<Solid::OpticalDrive>() && type == Eject) {
425 cerr << tr("Error: %1 does not have the interface OpticalDrive.").arg(udi) << endl;
426 return false;
427 }
428
429 switch (type) {
430 case Mount:
432 SIGNAL(setupDone(Solid::ErrorType, QVariant, QString)),
433 this,
434 SLOT(slotStorageResult(Solid::ErrorType, QVariant)));
435 device.as<Solid::StorageAccess>()->setup();
436 break;
437 case Unmount:
439 SIGNAL(teardownDone(Solid::ErrorType, QVariant, QString)),
440 this,
441 SLOT(slotStorageResult(Solid::ErrorType, QVariant)));
442 device.as<Solid::StorageAccess>()->teardown();
443 break;
444 case Eject:
446 SIGNAL(ejectDone(Solid::ErrorType, QVariant, QString)),
447 this,
448 SLOT(slotStorageResult(Solid::ErrorType, QVariant)));
449 device.as<Solid::OpticalDrive>()->eject();
450 break;
451 }
452
453 m_loop.exec();
454
455 if (m_error) {
456 cerr << tr("Error: %1").arg(m_errorString) << endl;
457 return false;
458 }
459
460 return true;
461}
462
463bool SolidHardware::listen()
464{
465 Solid::DeviceNotifier *notifier = Solid::DeviceNotifier::instance();
466 bool a = connect(notifier, SIGNAL(deviceAdded(QString)), this, SLOT(deviceAdded(QString)));
467 bool d = connect(notifier, SIGNAL(deviceRemoved(QString)), this, SLOT(deviceRemoved(QString)));
468
469 if (!a || !d) {
470 return false;
471 }
472
473 cout << "Listening to add/remove events: " << endl;
474 m_loop.exec();
475 return true;
476}
477
478bool SolidHardware::monitor(const QString &udi)
479{
480 Solid::Device device(udi);
481
482 if (!device.is<Solid::GenericInterface>())
483 return false;
484
485 auto genericInterface = device.as<Solid::GenericInterface>();
486
487 cout << "udi = '" << device.udi() << "'" << endl;
488 cout << genericInterface->allProperties();
489
491 this, [genericInterface](const auto &changes) {
492 cout << endl;
493 for (auto it = changes.begin(); it != changes.end(); ++it) {
494 cout << " " << it.key() << " = " << genericInterface->property(it.key()) << endl;
495 }
496 });
497
498 m_loop.exec();
499 return true;
500}
501
502void SolidHardware::deviceAdded(const QString &udi)
503{
504 cout << "Device Added:" << endl;
505 cout << "udi = '" << udi << "'" << endl;
506}
507
508void SolidHardware::deviceRemoved(const QString &udi)
509{
510 cout << "Device Removed:" << endl;
511 cout << "udi = '" << udi << "'" << endl;
512}
513
514void SolidHardware::slotStorageResult(Solid::ErrorType error, const QVariant &errorData)
515{
516 if (error) {
517 m_error = 1;
518 m_errorString = errorData.toString();
519 }
520 m_loop.exit();
521}
522
523#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.
Type type(const QSqlDatabase &db)
KSERVICE_EXPORT KService::List query(FilterFunc filterFunc)
QString path(const QString &relativePath)
KIOCORE_EXPORT QStringList list(const QString &fileClass)
KGuiItem properties()
const QList< QKeySequence > & end()
QDebug operator<<(QDebug dbg, const PerceptualColor::LchaDouble &value)
PHONON_EXPORT Notifier * notifier()
const char * constData() 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)
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 propertyCount() 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 mid(qsizetype position, qsizetype n) 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 Tue Mar 26 2024 11:17:12 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.