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> |
42 | using namespace std; |
43 | |
44 | static const char appName[] = "solid-hardware" ; |
45 | static const char programName[] = I18N_NOOP("solid-hardware" ); |
46 | |
47 | static const char description[] = I18N_NOOP("KDE tool for querying your hardware from the command line" ); |
48 | |
49 | static const char version[] = "0.1a" ; |
50 | |
51 | std::ostream &operator<<(std::ostream &out, const QString &msg) |
52 | { |
53 | return (out << msg.toLocal8Bit().constData()); |
54 | } |
55 | |
56 | std::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 | |
116 | std::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 | |
163 | std::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 | |
173 | void 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 | |
188 | int 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 | |
255 | bool 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 (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 | |
328 | bool 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 | |
350 | bool 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 | |
360 | bool 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 | |
373 | bool 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 | |
386 | bool 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 | |
437 | bool 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 | |
452 | void 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 | |
462 | void SolidHardware::slotPercent(KJob *job, unsigned long percent) |
463 | { |
464 | Q_UNUSED(job) |
465 | cout << i18n("Progress: %1%" , percent) << endl; |
466 | } |
467 | |
468 | void SolidHardware::slotInfoMessage(KJob *job, const QString &message) |
469 | { |
470 | Q_UNUSED(job) |
471 | cout << i18n("Info: %1" , message) << endl; |
472 | } |
473 | |
474 | void SolidHardware::deviceAdded(const QString &udi) |
475 | { |
476 | cout << "Device Added:" << endl; |
477 | cout << "udi = '" << udi << "'" << endl; |
478 | } |
479 | |
480 | void SolidHardware::deviceRemoved(const QString &udi) |
481 | { |
482 | cout << "Device Removed:" << endl; |
483 | cout << "udi = '" << udi << "'" << endl; |
484 | } |
485 | |
486 | |
487 | void 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 | |
500 | void 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 | |