1/* This file is part of the KDE project
2 Copyright (C) 2006 Kevin Ottens <ervin@kde.org>
3
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Library General Public
6 License version 2 as published by the Free Software Foundation.
7
8 This library is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 Library General Public License for more details.
12
13 You should have received a copy of the GNU Library General Public License
14 along with this library; see the file COPYING.LIB. If not, write to
15 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
16 Boston, MA 02110-1301, USA.
17
18*/
19
20#include "solid-hardware.h"
21
22
23#include <QString>
24#include <QStringList>
25#include <QMetaProperty>
26#include <QMetaEnum>
27#include <QTimer>
28
29#include <kcomponentdata.h>
30#include <kcmdlineargs.h>
31#include <kdebug.h>
32#include <kjob.h>
33#include <klocale.h>
34
35#include <solid/device.h>
36#include <solid/genericinterface.h>
37#include <solid/storageaccess.h>
38#include <solid/opticaldrive.h>
39
40#include <iostream>
41#include <solid/devicenotifier.h>
42using namespace std;
43
44static const char appName[] = "solid-hardware";
45static const char programName[] = I18N_NOOP("solid-hardware");
46
47static const char description[] = I18N_NOOP("KDE tool for querying your hardware from the command line");
48
49static const char version[] = "0.1a";
50
51std::ostream &operator<<(std::ostream &out, const QString &msg)
52{
53 return (out << msg.toLocal8Bit().constData());
54}
55
56std::ostream &operator<<(std::ostream &out, const QVariant &value)
57{
58 switch (value.type())
59 {
60 case QVariant::StringList:
61 {
62 out << "{";
63
64 const QStringList list = value.toStringList();
65
66 QStringList::ConstIterator it = list.constBegin();
67 QStringList::ConstIterator end = list.constEnd();
68
69 for (; it!=end; ++it)
70 {
71 out << "'" << *it << "'";
72
73 if (it+1!=end)
74 {
75 out << ", ";
76 }
77 }
78
79 out << "} (string list)";
80 break;
81 }
82 case QVariant::Bool:
83 out << (value.toBool()?"true":"false") << " (bool)";
84 break;
85 case QVariant::Int:
86 case QVariant::LongLong:
87 out << value.toString()
88 << " (0x" << QString::number(value.toLongLong(), 16) << ") (" << QVariant::typeToName(value.type()) << ")";
89 break;
90 case QVariant::UInt:
91 case QVariant::ULongLong:
92 out << value.toString()
93 << " (0x" << QString::number(value.toULongLong(), 16) << ") (" << QVariant::typeToName(value.type()) << ")";
94 break;
95 case QVariant::UserType:
96 {
97 //qDebug() << "got variant type:" << value.typeName();
98 if (value.canConvert<QList<int> >())
99 {
100 QList<int> intlist = value.value<QList<int> >();
101 QStringList tmp;
102 foreach (int val, intlist)
103 tmp.append(QString::number(val));
104 out << "{" << tmp.join(",") << "} (int list)";
105 }
106 break;
107 }
108 default:
109 out << "'" << value.toString() << "' (string)";
110 break;
111 }
112
113 return out;
114}
115
116std::ostream &operator<<(std::ostream &out, const Solid::Device &device)
117{
118 out << " parent = " << QVariant(device.parentUdi()) << endl;
119 out << " vendor = " << QVariant(device.vendor()) << endl;
120 out << " product = " << QVariant(device.product()) << endl;
121 out << " description = " << QVariant(device.description()) << endl;
122
123 int index = Solid::DeviceInterface::staticMetaObject.indexOfEnumerator("Type");
124 QMetaEnum typeEnum = Solid::DeviceInterface::staticMetaObject.enumerator(index);
125
126 for (int i=0; i<typeEnum.keyCount(); i++)
127 {
128 Solid::DeviceInterface::Type type = (Solid::DeviceInterface::Type)typeEnum.value(i);
129 const Solid::DeviceInterface *interface = device.asDeviceInterface(type);
130
131 if (interface)
132 {
133 const QMetaObject *meta = interface->metaObject();
134
135 for (int i=meta->propertyOffset(); i<meta->propertyCount(); i++)
136 {
137 QMetaProperty property = meta->property(i);
138 out << " " << QString(meta->className()).mid(7) << "." << property.name()
139 << " = ";
140
141 QVariant value = property.read(interface);
142
143 if (property.isEnumType()) {
144 QMetaEnum metaEnum = property.enumerator();
145 if (metaEnum.isFlag()) {
146 out << "'" << metaEnum.valueToKeys(value.toInt()).constData() << "'"
147 << " (0x" << QString::number(value.toInt(), 16) << ") (flag)";
148 } else {
149 out << "'" << metaEnum.valueToKey(value.toInt()) << "'"
150 << " (0x" << QString::number(value.toInt(), 16) << ") (enum)";
151 }
152 out << endl;
153 } else {
154 out << value << endl;
155 }
156 }
157 }
158 }
159
160 return out;
161}
162
163std::ostream &operator<<(std::ostream &out, const QMap<QString,QVariant> &properties)
164{
165 foreach (const QString &key, properties.keys())
166 {
167 out << " " << key << " = " << properties[key] << endl;
168 }
169
170 return out;
171}
172
173void checkArgumentCount(int min, int max)
174{
175 int count = KCmdLineArgs::parsedArgs()->count();
176
177 if (count < min)
178 {
179 KCmdLineArgs::usageError(i18n("Syntax Error: Not enough arguments"));
180 }
181
182 if ((max > 0) && (count > max))
183 {
184 KCmdLineArgs::usageError(i18n("Syntax Error: Too many arguments"));
185 }
186}
187
188int main(int argc, char **argv)
189{
190 KCmdLineArgs::init(argc, argv, appName, 0, ki18n(programName), version, ki18n(description), KCmdLineArgs::CmdLineArgNone);
191
192
193 KCmdLineOptions options;
194
195 options.add("commands", ki18n("Show available commands"));
196
197 options.add("+command", ki18n("Command (see --commands)"));
198
199 options.add("+[arg(s)]", ki18n("Arguments for command"));
200
201 KCmdLineArgs::addCmdLineOptions(options);
202
203 KCmdLineArgs *args = KCmdLineArgs::parsedArgs();
204
205 KComponentData componentData(appName);
206
207 if (args->isSet("commands"))
208 {
209 KCmdLineArgs::enable_i18n();
210
211 cout << endl << i18n("Syntax:") << endl << endl;
212
213 cout << " solid-hardware list [details|nonportableinfo]" << endl;
214 cout << i18n(" # List the hardware available in the system.\n"
215 " # - If the 'nonportableinfo' option is specified, the device\n"
216 " # properties are listed (be careful, in this case property names\n"
217 " # are backend dependent),\n"
218 " # - If the 'details' option is specified, the device interfaces\n"
219 " # and the corresponding properties are listed in a platform\n"
220 " # neutral fashion,\n"
221 " # - Otherwise only device UDIs are listed.\n") << endl;
222
223 cout << " solid-hardware details 'udi'" << endl;
224 cout << i18n(" # Display all the interfaces and properties of the device\n"
225 " # corresponding to 'udi' in a platform neutral fashion.\n") << endl;
226
227 cout << " solid-hardware nonportableinfo 'udi'" << endl;
228 cout << i18n(" # Display all the properties of the device corresponding to 'udi'\n"
229 " # (be careful, in this case property names are backend dependent).\n") << endl;
230
231 cout << " solid-hardware query 'predicate' ['parentUdi']" << endl;
232 cout << i18n(" # List the UDI of devices corresponding to 'predicate'.\n"
233 " # - If 'parentUdi' is specified, the search is restricted to the\n"
234 " # branch of the corresponding device,\n"
235 " # - Otherwise the search is done on all the devices.\n") << endl;
236
237 cout << " solid-hardware mount 'udi'" << endl;
238 cout << i18n(" # If applicable, mount the device corresponding to 'udi'.\n") << endl;
239
240 cout << " solid-hardware unmount 'udi'" << endl;
241 cout << i18n(" # If applicable, unmount the device corresponding to 'udi'.\n") << endl;
242
243 cout << " solid-hardware eject 'udi'" << endl;
244 cout << i18n(" # If applicable, eject the device corresponding to 'udi'.\n") << endl;
245
246 cout << " solid-hardware listen" << endl;
247 cout << i18n(" # Listen to all add/remove events on supported hardware.") << endl;
248
249 return 0;
250 }
251
252 return SolidHardware::doIt() ? 0 : 1;
253}
254
255bool SolidHardware::doIt()
256{
257 KCmdLineArgs *args = KCmdLineArgs::parsedArgs();
258 checkArgumentCount(1, 0);
259
260 QString command(args->arg(0));
261
262 int fake_argc = 0;
263 char **fake_argv = 0;
264 SolidHardware shell(fake_argc, fake_argv);
265
266 if (command == "list")
267 {
268 checkArgumentCount(1, 2);
269 QByteArray extra(args->count()==2 ? args->arg(1).toLocal8Bit() : "");
270 return shell.hwList(extra=="details", extra=="nonportableinfo");
271 }
272 else if (command == "details")
273 {
274 checkArgumentCount(2, 2);
275 QString udi(args->arg(1));
276 return shell.hwCapabilities(udi);
277 }
278 else if (command == "nonportableinfo")
279 {
280 checkArgumentCount(2, 2);
281 QString udi(args->arg(1));
282 return shell.hwProperties(udi);
283 }
284 else if (command == "query")
285 {
286 checkArgumentCount(2, 3);
287
288 QString query = args->arg(1);
289 QString parent;
290
291 if (args->count() == 3)
292 {
293 parent = args->arg(2);
294 }
295
296 return shell.hwQuery(parent, query);
297 }
298 else if (command == "mount")
299 {
300 checkArgumentCount(2, 2);
301 QString udi(args->arg(1));
302 return shell.hwVolumeCall(Mount, udi);
303 }
304 else if (command == "unmount")
305 {
306 checkArgumentCount(2, 2);
307 QString udi(args->arg(1));
308 return shell.hwVolumeCall(Unmount, udi);
309 }
310 else if (command == "eject")
311 {
312 checkArgumentCount(2, 2);
313 QString udi(args->arg(1));
314 return shell.hwVolumeCall(Eject, udi);
315 }
316 else if (command == "listen")
317 {
318 return shell.listen();
319 }
320 else
321 {
322 cerr << i18n("Syntax Error: Unknown command '%1'" ,command) << endl;
323 }
324
325 return false;
326}
327
328bool SolidHardware::hwList(bool interfaces, bool system)
329{
330 const QList<Solid::Device> all = Solid::Device::allDevices();
331
332 foreach (const Solid::Device &device, all)
333 {
334 cout << "udi = '" << device.udi() << "'" << endl;
335
336 if (interfaces)
337 {
338 cout << device << endl;
339 }
340 else if (system && device.is<Solid::GenericInterface>())
341 {
342 QMap<QString,QVariant> properties = device.as<Solid::GenericInterface>()->allProperties();
343 cout << properties << endl;
344 }
345 }
346
347 return true;
348}
349
350bool SolidHardware::hwCapabilities(const QString &udi)
351{
352 const Solid::Device device(udi);
353
354 cout << "udi = '" << device.udi() << "'" << endl;
355 cout << device << endl;
356
357 return true;
358}
359
360bool SolidHardware::hwProperties(const QString &udi)
361{
362 const Solid::Device device(udi);
363
364 cout << "udi = '" << device.udi() << "'" << endl;
365 if (device.is<Solid::GenericInterface>()) {
366 QMap<QString,QVariant> properties = device.as<Solid::GenericInterface>()->allProperties();
367 cout << properties << endl;
368 }
369
370 return true;
371}
372
373bool SolidHardware::hwQuery(const QString &parentUdi, const QString &query)
374{
375 const QList<Solid::Device> devices
376 = Solid::Device::listFromQuery(query, parentUdi);
377
378 foreach (const Solid::Device &device, devices)
379 {
380 cout << "udi = '" << device.udi() << "'" << endl;
381 }
382
383 return true;
384}
385
386bool SolidHardware::hwVolumeCall(SolidHardware::VolumeCallType type, const QString &udi)
387{
388 Solid::Device device(udi);
389
390 if (!device.is<Solid::StorageAccess>() && type!=Eject)
391 {
392 cerr << i18n("Error: %1 does not have the interface StorageAccess." , udi) << endl;
393 return false;
394 }
395 else if (!device.is<Solid::OpticalDrive>() && type==Eject)
396 {
397 cerr << i18n("Error: %1 does not have the interface OpticalDrive." , udi) << endl;
398 return false;
399 }
400
401 switch(type)
402 {
403 case Mount:
404 connect(device.as<Solid::StorageAccess>(),
405 SIGNAL(setupDone(Solid::ErrorType, QVariant, const QString &)),
406 this,
407 SLOT(slotStorageResult(Solid::ErrorType, QVariant)));
408 device.as<Solid::StorageAccess>()->setup();
409 break;
410 case Unmount:
411 connect(device.as<Solid::StorageAccess>(),
412 SIGNAL(teardownDone(Solid::ErrorType, QVariant, const QString &)),
413 this,
414 SLOT(slotStorageResult(Solid::ErrorType, QVariant)));
415 device.as<Solid::StorageAccess>()->teardown();
416 break;
417 case Eject:
418 connect(device.as<Solid::OpticalDrive>(),
419 SIGNAL(ejectDone(Solid::ErrorType, QVariant, const QString &)),
420 this,
421 SLOT(slotStorageResult(Solid::ErrorType, QVariant)));
422 device.as<Solid::OpticalDrive>()->eject();
423 break;
424 }
425
426 m_loop.exec();
427
428 if (m_error)
429 {
430 cerr << i18n("Error: %1" , m_errorString) << endl;
431 return false;
432 }
433
434 return true;
435}
436
437bool SolidHardware::listen()
438{
439 Solid::DeviceNotifier *notifier = Solid::DeviceNotifier::instance();
440 bool a = connect(notifier, SIGNAL(deviceAdded(QString)), this, SLOT(deviceAdded(QString)));
441 bool d = connect(notifier, SIGNAL(deviceRemoved(QString)), this, SLOT(deviceRemoved(QString)));
442
443 if (!a || !d) {
444 return false;
445 }
446
447 cout << "Listening to add/remove events: " << endl;
448 m_loop.exec();
449 return true;
450}
451
452void SolidHardware::connectJob(KJob *job)
453{
454 connect(job, SIGNAL(result(KJob *)),
455 this, SLOT(slotResult(KJob *)));
456 connect(job, SIGNAL(percent(KJob *, unsigned long)),
457 this, SLOT(slotPercent(KJob *, unsigned long)));
458 connect(job, SIGNAL(infoMessage(KJob *, const QString &, const QString &)),
459 this, SLOT(slotInfoMessage(KJob *, const QString &)));
460}
461
462void SolidHardware::slotPercent(KJob *job, unsigned long percent)
463{
464 Q_UNUSED(job)
465 cout << i18n("Progress: %1%" , percent) << endl;
466}
467
468void SolidHardware::slotInfoMessage(KJob *job, const QString &message)
469{
470 Q_UNUSED(job)
471 cout << i18n("Info: %1" , message) << endl;
472}
473
474void SolidHardware::deviceAdded(const QString &udi)
475{
476 cout << "Device Added:" << endl;
477 cout << "udi = '" << udi << "'" << endl;
478}
479
480void SolidHardware::deviceRemoved(const QString &udi)
481{
482 cout << "Device Removed:" << endl;
483 cout << "udi = '" << udi << "'" << endl;
484}
485
486
487void SolidHardware::slotResult(KJob *job)
488{
489 m_error = 0;
490
491 if (job->error())
492 {
493 m_error = job->error();
494 m_errorString = job->errorString();
495 }
496
497 m_loop.exit();
498}
499
500void SolidHardware::slotStorageResult(Solid::ErrorType error, const QVariant &errorData)
501{
502 if (error) {
503 m_error = 1;
504 m_errorString = errorData.toString();
505 }
506 m_loop.exit();
507}
508
509#include "solid-hardware.moc"
510