File indexing completed on 2024-05-05 04:01:21
0001 /* This file is part of the KDE project 0002 SPDX-FileCopyrightText: 2006 Kevin Ottens <ervin@kde.org> 0003 SPDX-FileCopyrightText: 2014 Alejandro Fiestas Olivares <afiestas@kde.org> 0004 0005 SPDX-License-Identifier: LGPL-2.1-or-later 0006 */ 0007 0008 #include "solid-hardware.h" 0009 0010 #if defined QT_DBUS_LIB 0011 #include <QDBusArgument> 0012 #include <QDBusObjectPath> 0013 #endif 0014 #include <QMetaEnum> 0015 #include <QMetaProperty> 0016 #include <QString> 0017 #include <QStringList> 0018 #include <QTextStream> 0019 0020 #include <QCommandLineParser> 0021 0022 #include <solid/device.h> 0023 #include <solid/genericinterface.h> 0024 #include <solid/opticaldrive.h> 0025 0026 #include <iostream> 0027 #include <solid/devicenotifier.h> 0028 using namespace std; 0029 0030 static const char appName[] = "solid-hardware"; 0031 0032 static const char version[] = "0.1a"; 0033 0034 std::ostream &operator<<(std::ostream &out, const QString &msg) 0035 { 0036 return (out << msg.toLocal8Bit().constData()); 0037 } 0038 0039 std::ostream &operator<<(std::ostream &out, const QVariant &value); 0040 #if defined QT_DBUS_LIB 0041 std::ostream &operator<<(std::ostream &out, const QDBusArgument &arg) 0042 { 0043 auto type = arg.currentType(); 0044 switch (type) { 0045 case QDBusArgument::ArrayType: 0046 out << " { "; 0047 arg.beginArray(); 0048 while (!arg.atEnd()) { 0049 out << arg; 0050 if (!arg.atEnd()) { 0051 out << ", "; 0052 } 0053 } 0054 arg.endArray(); 0055 out << " }"; 0056 break; 0057 case QDBusArgument::StructureType: 0058 out << " ( "; 0059 arg.beginStructure(); 0060 while (!arg.atEnd()) { 0061 out << arg.asVariant(); 0062 } 0063 arg.endStructure(); 0064 out << " )"; 0065 break; 0066 case QDBusArgument::MapType: 0067 out << " [ "; 0068 arg.beginMap(); 0069 if (!arg.atEnd()) { 0070 out << arg; 0071 } 0072 arg.endMap(); 0073 out << " ]"; 0074 break; 0075 case QDBusArgument::MapEntryType: 0076 arg.beginMapEntry(); 0077 out << arg.asVariant() << " = " << arg; 0078 arg.endMapEntry(); 0079 break; 0080 case QDBusArgument::UnknownType: 0081 out << "(unknown DBus type)"; 0082 break; 0083 case QDBusArgument::BasicType: 0084 case QDBusArgument::VariantType: 0085 out << arg.asVariant(); 0086 } 0087 return out; 0088 } 0089 #endif 0090 0091 std::ostream &operator<<(std::ostream &out, const QVariant &value) 0092 { 0093 switch (value.userType()) { 0094 case QMetaType::QStringList: { 0095 out << "{"; 0096 0097 const QStringList list = value.toStringList(); 0098 0099 QStringList::ConstIterator it = list.constBegin(); 0100 QStringList::ConstIterator end = list.constEnd(); 0101 0102 for (; it != end; ++it) { 0103 out << "'" << *it << "'"; 0104 0105 if (it + 1 != end) { 0106 out << ", "; 0107 } 0108 } 0109 0110 out << "} (string list)"; 0111 break; 0112 } 0113 case QMetaType::QString: 0114 out << "'" << value.toString() << "' (string)"; 0115 break; 0116 case QMetaType::Bool: 0117 out << (value.toBool() ? "true" : "false") << " (bool)"; 0118 break; 0119 case QMetaType::Int: 0120 case QMetaType::LongLong: 0121 out << value.toString() << " (0x" << QString::number(value.toLongLong(), 16) << ") (" << value.typeName() << ")"; 0122 break; 0123 case QMetaType::UInt: 0124 case QMetaType::ULongLong: 0125 out << value.toString() << " (0x" << QString::number(value.toULongLong(), 16) << ") (" << value.typeName() << ")"; 0126 break; 0127 case QMetaType::Double: 0128 out << value.toString() << " (double)"; 0129 break; 0130 case QMetaType::QByteArray: 0131 out << "'" << value.toString() << "' (bytes)"; 0132 break; 0133 case QMetaType::User: 0134 // qDebug() << "got variant type:" << value.typeName(); 0135 if (value.canConvert<QList<int>>()) { 0136 const QList<int> intlist = value.value<QList<int>>(); 0137 QStringList tmp; 0138 for (const int val : intlist) { 0139 tmp.append(QString::number(val)); 0140 } 0141 out << "{" << tmp.join(",") << "} (int list)"; 0142 #if defined QT_DBUS_LIB 0143 } else if (value.canConvert<QDBusObjectPath>()) { 0144 out << value.value<QDBusObjectPath>().path() << " (ObjectPath)"; 0145 } else if (value.canConvert<QDBusVariant>()) { 0146 out << value.value<QDBusVariant>().variant() << "(Variant)"; 0147 } else if (value.canConvert<QDBusArgument>()) { 0148 out << value.value<QDBusArgument>(); 0149 #endif 0150 } else { 0151 out << value.toString() << " (unhandled)"; 0152 } 0153 0154 break; 0155 default: 0156 out << "'" << value.toString() << "' (" << value.typeName() << ")"; 0157 break; 0158 } 0159 0160 return out; 0161 } 0162 0163 std::ostream &operator<<(std::ostream &out, const Solid::Device &device) 0164 { 0165 out << " parent = " << QVariant(device.parentUdi()) << endl; 0166 out << " vendor = " << QVariant(device.vendor()) << endl; 0167 out << " product = " << QVariant(device.product()) << endl; 0168 out << " description = " << QVariant(device.description()) << endl; 0169 out << " icon = " << QVariant(device.icon()) << endl; 0170 0171 int index = Solid::DeviceInterface::staticMetaObject.indexOfEnumerator("Type"); 0172 QMetaEnum typeEnum = Solid::DeviceInterface::staticMetaObject.enumerator(index); 0173 0174 for (int i = 0; i < typeEnum.keyCount(); i++) { 0175 Solid::DeviceInterface::Type type = (Solid::DeviceInterface::Type)typeEnum.value(i); 0176 const Solid::DeviceInterface *interface = device.asDeviceInterface(type); 0177 0178 if (interface) { 0179 const QMetaObject *meta = interface->metaObject(); 0180 0181 for (int i = meta->propertyOffset(); i < meta->propertyCount(); i++) { 0182 QMetaProperty property = meta->property(i); 0183 out << " " << QString(meta->className()).mid(7) << "." << property.name() << " = "; 0184 0185 QVariant value = property.read(interface); 0186 0187 if (property.isEnumType()) { 0188 QMetaEnum metaEnum = property.enumerator(); 0189 if (metaEnum.isFlag()) { 0190 out << "'" << metaEnum.valueToKeys(value.toInt()).constData() << "'" 0191 << " (0x" << QString::number(value.toInt(), 16) << ") (flag)"; 0192 } else { 0193 out << "'" << metaEnum.valueToKey(value.toInt()) << "'" 0194 << " (0x" << QString::number(value.toInt(), 16) << ") (enum)"; 0195 } 0196 out << endl; 0197 } else { 0198 out << value << endl; 0199 } 0200 } 0201 } 0202 } 0203 0204 return out; 0205 } 0206 0207 std::ostream &operator<<(std::ostream &out, const QMap<QString, QVariant> &properties) 0208 { 0209 for (auto it = properties.cbegin(); it != properties.cend(); ++it) { 0210 out << " " << it.key() << " = " << it.value() << endl; 0211 } 0212 0213 return out; 0214 } 0215 0216 QString getUdiFromArguments(QCoreApplication &app, QCommandLineParser &parser) 0217 { 0218 parser.addPositionalArgument("udi", QCoreApplication::translate("solid-hardware", "Device udi")); 0219 parser.process(app); 0220 if (parser.positionalArguments().count() < 2) { 0221 parser.showHelp(1); 0222 } 0223 return parser.positionalArguments().at(1); 0224 } 0225 0226 static QString commandsHelp() 0227 { 0228 QString data; 0229 QTextStream cout(&data); 0230 cout << '\n' << QCoreApplication::translate("solid-hardware", "Syntax:") << '\n' << '\n'; 0231 0232 cout << " solid-hardware list [details|nonportableinfo]" << '\n'; 0233 cout << QCoreApplication::translate("solid-hardware", 0234 " # List the hardware available in the system.\n" 0235 " # - If the 'nonportableinfo' option is specified, the device\n" 0236 " # properties are listed (be careful, in this case property names\n" 0237 " # are backend dependent),\n" 0238 " # - If the 'details' option is specified, the device interfaces\n" 0239 " # and the corresponding properties are listed in a platform\n" 0240 " # neutral fashion,\n" 0241 " # - Otherwise only device UDIs are listed.\n") 0242 << '\n'; 0243 0244 cout << " solid-hardware details 'udi'" << '\n'; 0245 cout << QCoreApplication::translate("solid-hardware", 0246 " # Display all the interfaces and properties of the device\n" 0247 " # corresponding to 'udi' in a platform neutral fashion.\n") 0248 << '\n'; 0249 0250 cout << " solid-hardware nonportableinfo 'udi'" << '\n'; 0251 cout << QCoreApplication::translate("solid-hardware", 0252 " # Display all the properties of the device corresponding to 'udi'\n" 0253 " # (be careful, in this case property names are backend dependent).\n") 0254 << '\n'; 0255 0256 cout << " solid-hardware query 'predicate' ['parentUdi']" << '\n'; 0257 cout << QCoreApplication::translate("solid-hardware", 0258 " # List the UDI of devices corresponding to 'predicate'.\n" 0259 " # - If 'parentUdi' is specified, the search is restricted to the\n" 0260 " # branch of the corresponding device,\n" 0261 " # - Otherwise the search is done on all the devices.\n") 0262 << '\n'; 0263 0264 cout << " solid-hardware mount 'udi'" << '\n'; 0265 cout << QCoreApplication::translate("solid-hardware", " # If applicable, mount the device corresponding to 'udi'.\n") << '\n'; 0266 0267 cout << " solid-hardware unmount 'udi'" << '\n'; 0268 cout << QCoreApplication::translate("solid-hardware", " # If applicable, unmount the device corresponding to 'udi'.\n") << '\n'; 0269 0270 cout << " solid-hardware eject 'udi'" << '\n'; 0271 cout << QCoreApplication::translate("solid-hardware", " # If applicable, eject the device corresponding to 'udi'.\n") << '\n'; 0272 0273 cout << " solid-hardware listen" << '\n'; 0274 cout << QCoreApplication::translate("solid-hardware", " # Listen to all add/remove events on supported hardware.\n") << '\n'; 0275 0276 cout << " solid-hardware monitor 'udi'" << '\n'; 0277 cout << QCoreApplication::translate("solid-hardware", " # Monitor devices for changes.\n"); 0278 0279 return data; 0280 } 0281 0282 int main(int argc, char **argv) 0283 { 0284 SolidHardware app(argc, argv); 0285 app.setApplicationName(appName); 0286 app.setApplicationVersion(version); 0287 0288 QCommandLineParser parser; 0289 parser.setApplicationDescription(QCoreApplication::translate("solid-hardware", "KDE tool for querying your hardware from the command line")); 0290 parser.addHelpOption(); 0291 parser.addVersionOption(); 0292 parser.addPositionalArgument("command", QCoreApplication::translate("solid-hardware", "Command to execute"), commandsHelp()); 0293 0294 QCommandLineOption commands("commands", QCoreApplication::translate("solid-hardware", "Show available commands")); 0295 // --commands only for backwards compat, it's now in the "syntax help" 0296 // of the positional argument. 0297 commands.setFlags(QCommandLineOption::HiddenFromHelp); 0298 parser.addOption(commands); 0299 0300 parser.process(app); 0301 if (parser.isSet(commands)) { 0302 cout << commandsHelp() << endl; 0303 return 0; 0304 } 0305 0306 QStringList args = parser.positionalArguments(); 0307 if (args.count() < 1) { 0308 parser.showHelp(1); 0309 } 0310 0311 parser.clearPositionalArguments(); 0312 0313 QString command(args.at(0)); 0314 0315 if (command == "list") { 0316 parser.addPositionalArgument("details", QCoreApplication::translate("solid-hardware", "Show device details")); 0317 parser.addPositionalArgument("nonportableinfo", QCoreApplication::translate("solid-hardware", "Show non portable information")); 0318 parser.process(app); 0319 args = parser.positionalArguments(); 0320 QByteArray extra(args.count() == 2 ? args.at(1).toLocal8Bit() : QByteArray()); 0321 return app.hwList(extra == "details", extra == "nonportableinfo"); 0322 } else if (command == "details") { 0323 const QString udi = getUdiFromArguments(app, parser); 0324 return app.hwCapabilities(udi); 0325 } else if (command == "nonportableinfo") { 0326 const QString udi = getUdiFromArguments(app, parser); 0327 return app.hwProperties(udi); 0328 } else if (command == "query") { 0329 parser.addPositionalArgument("udi", QCoreApplication::translate("solid-hardware", "Device udi")); 0330 parser.addPositionalArgument("parent", QCoreApplication::translate("solid-hardware", "Parent device udi")); 0331 parser.process(app); 0332 if (parser.positionalArguments().count() < 2 || parser.positionalArguments().count() > 3) { 0333 parser.showHelp(1); 0334 } 0335 0336 QString query = args.at(1); 0337 QString parent; 0338 0339 if (args.count() == 3) { 0340 parent = args.at(2); 0341 } 0342 0343 return app.hwQuery(parent, query); 0344 } else if (command == "mount") { 0345 const QString udi = getUdiFromArguments(app, parser); 0346 return app.hwVolumeCall(SolidHardware::Mount, udi); 0347 } else if (command == "unmount") { 0348 const QString udi = getUdiFromArguments(app, parser); 0349 return app.hwVolumeCall(SolidHardware::Unmount, udi); 0350 } else if (command == "eject") { 0351 const QString udi = getUdiFromArguments(app, parser); 0352 return app.hwVolumeCall(SolidHardware::Eject, udi); 0353 } else if (command == "listen") { 0354 return app.listen(); 0355 } else if (command == "monitor") { 0356 const QString udi = getUdiFromArguments(app, parser); 0357 return app.monitor(udi); 0358 } 0359 0360 cerr << QCoreApplication::translate("solid-hardware", "Syntax Error: Unknown command '%1'").arg(command) << endl; 0361 0362 return 1; 0363 } 0364 0365 bool SolidHardware::hwList(bool interfaces, bool system) 0366 { 0367 const QList<Solid::Device> all = Solid::Device::allDevices(); 0368 0369 for (const Solid::Device &device : all) { 0370 cout << "udi = '" << device.udi() << "'" << endl; 0371 0372 if (interfaces) { 0373 cout << device << endl; 0374 } else if (system && device.is<Solid::GenericInterface>()) { 0375 QMap<QString, QVariant> properties = device.as<Solid::GenericInterface>()->allProperties(); 0376 cout << properties << endl; 0377 } 0378 } 0379 0380 return true; 0381 } 0382 0383 bool SolidHardware::hwCapabilities(const QString &udi) 0384 { 0385 const Solid::Device device(udi); 0386 0387 cout << "udi = '" << device.udi() << "'" << endl; 0388 cout << device << endl; 0389 0390 return true; 0391 } 0392 0393 bool SolidHardware::hwProperties(const QString &udi) 0394 { 0395 const Solid::Device device(udi); 0396 0397 cout << "udi = '" << device.udi() << "'" << endl; 0398 if (device.is<Solid::GenericInterface>()) { 0399 QMap<QString, QVariant> properties = device.as<Solid::GenericInterface>()->allProperties(); 0400 cout << properties << endl; 0401 } 0402 0403 return true; 0404 } 0405 0406 bool SolidHardware::hwQuery(const QString &parentUdi, const QString &query) 0407 { 0408 const QList<Solid::Device> devices = Solid::Device::listFromQuery(query, parentUdi); 0409 0410 for (const Solid::Device &device : devices) { 0411 cout << "udi = '" << device.udi() << "'" << endl; 0412 } 0413 0414 return true; 0415 } 0416 0417 bool SolidHardware::hwVolumeCall(SolidHardware::VolumeCallType type, const QString &udi) 0418 { 0419 Solid::Device device(udi); 0420 0421 if (!device.is<Solid::StorageAccess>() && type != Eject) { 0422 cerr << tr("Error: %1 does not have the interface StorageAccess.").arg(udi) << endl; 0423 return false; 0424 } else if (!device.is<Solid::OpticalDrive>() && type == Eject) { 0425 cerr << tr("Error: %1 does not have the interface OpticalDrive.").arg(udi) << endl; 0426 return false; 0427 } 0428 0429 switch (type) { 0430 case Mount: 0431 connect(device.as<Solid::StorageAccess>(), 0432 SIGNAL(setupDone(Solid::ErrorType, QVariant, QString)), 0433 this, 0434 SLOT(slotStorageResult(Solid::ErrorType, QVariant))); 0435 device.as<Solid::StorageAccess>()->setup(); 0436 break; 0437 case Unmount: 0438 connect(device.as<Solid::StorageAccess>(), 0439 SIGNAL(teardownDone(Solid::ErrorType, QVariant, QString)), 0440 this, 0441 SLOT(slotStorageResult(Solid::ErrorType, QVariant))); 0442 device.as<Solid::StorageAccess>()->teardown(); 0443 break; 0444 case Eject: 0445 connect(device.as<Solid::OpticalDrive>(), 0446 SIGNAL(ejectDone(Solid::ErrorType, QVariant, QString)), 0447 this, 0448 SLOT(slotStorageResult(Solid::ErrorType, QVariant))); 0449 device.as<Solid::OpticalDrive>()->eject(); 0450 break; 0451 } 0452 0453 m_loop.exec(); 0454 0455 if (m_error) { 0456 cerr << tr("Error: %1").arg(m_errorString) << endl; 0457 return false; 0458 } 0459 0460 return true; 0461 } 0462 0463 bool SolidHardware::listen() 0464 { 0465 Solid::DeviceNotifier *notifier = Solid::DeviceNotifier::instance(); 0466 bool a = connect(notifier, SIGNAL(deviceAdded(QString)), this, SLOT(deviceAdded(QString))); 0467 bool d = connect(notifier, SIGNAL(deviceRemoved(QString)), this, SLOT(deviceRemoved(QString))); 0468 0469 if (!a || !d) { 0470 return false; 0471 } 0472 0473 cout << "Listening to add/remove events: " << endl; 0474 m_loop.exec(); 0475 return true; 0476 } 0477 0478 bool SolidHardware::monitor(const QString &udi) 0479 { 0480 Solid::Device device(udi); 0481 0482 if (!device.is<Solid::GenericInterface>()) 0483 return false; 0484 0485 auto genericInterface = device.as<Solid::GenericInterface>(); 0486 0487 cout << "udi = '" << device.udi() << "'" << endl; 0488 cout << genericInterface->allProperties(); 0489 0490 connect(genericInterface, &Solid::GenericInterface::propertyChanged, 0491 this, [genericInterface](const auto &changes) { 0492 cout << endl; 0493 for (auto it = changes.begin(); it != changes.end(); ++it) { 0494 cout << " " << it.key() << " = " << genericInterface->property(it.key()) << endl; 0495 } 0496 }); 0497 0498 m_loop.exec(); 0499 return true; 0500 } 0501 0502 void SolidHardware::deviceAdded(const QString &udi) 0503 { 0504 cout << "Device Added:" << endl; 0505 cout << "udi = '" << udi << "'" << endl; 0506 } 0507 0508 void SolidHardware::deviceRemoved(const QString &udi) 0509 { 0510 cout << "Device Removed:" << endl; 0511 cout << "udi = '" << udi << "'" << endl; 0512 } 0513 0514 void SolidHardware::slotStorageResult(Solid::ErrorType error, const QVariant &errorData) 0515 { 0516 if (error) { 0517 m_error = 1; 0518 m_errorString = errorData.toString(); 0519 } 0520 m_loop.exit(); 0521 } 0522 0523 #include "moc_solid-hardware.cpp"