source: trunk/tools/qdbus/qdbus/qdbus.cpp

Last change on this file was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

File size: 19.5 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation (qt-info@nokia.com)
6**
7** This file is part of the tools applications of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
15**
16** GNU Lesser General Public License Usage
17** Alternatively, this file may be used under the terms of the GNU Lesser
18** General Public License version 2.1 as published by the Free Software
19** Foundation and appearing in the file LICENSE.LGPL included in the
20** packaging of this file. Please review the following information to
21** ensure the GNU Lesser General Public License version 2.1 requirements
22** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23**
24** In addition, as a special exception, Nokia gives you certain additional
25** rights. These rights are described in the Nokia Qt LGPL Exception
26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you have questions regarding the use of this file, please contact
37** Nokia at qt-info@nokia.com.
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include <stdio.h>
43#include <stdlib.h>
44
45#include <QtCore/QCoreApplication>
46#include <QtCore/QStringList>
47#include <QtCore/qmetaobject.h>
48#include <QtXml/QDomDocument>
49#include <QtXml/QDomElement>
50#include <QtDBus/QtDBus>
51#include <private/qdbusutil_p.h>
52
53static QDBusConnection connection(QLatin1String(""));
54static bool printArgumentsLiterally = false;
55
56static void showUsage()
57{
58 printf("Usage: qdbus [--system] [--literal] [servicename] [path] [method] [args]\n"
59 "\n"
60 " servicename the service to connect to (e.g., org.freedesktop.DBus)\n"
61 " path the path to the object (e.g., /)\n"
62 " method the method to call, with or without the interface\n"
63 " args arguments to pass to the call\n"
64 "With 0 arguments, qdbus will list the services available on the bus\n"
65 "With just the servicename, qdbus will list the object paths available on the service\n"
66 "With service name and object path, qdbus will list the methods, signals and properties available on the object\n"
67 "\n"
68 "Options:\n"
69 " --system connect to the system bus\n"
70 " --literal print replies literally\n");
71}
72
73static void printArg(const QVariant &v)
74{
75 if (printArgumentsLiterally) {
76 printf("%s\n", qPrintable(QDBusUtil::argumentToString(v)));
77 return;
78 }
79
80 if (v.userType() == QVariant::StringList) {
81 foreach (QString s, v.toStringList())
82 printf("%s\n", qPrintable(s));
83 } else if (v.userType() == QVariant::List) {
84 foreach (const QVariant &var, v.toList())
85 printArg(var);
86 } else if (v.userType() == QVariant::Map) {
87 const QVariantMap map = v.toMap();
88 QVariantMap::ConstIterator it = map.constBegin();
89 for ( ; it != map.constEnd(); ++it) {
90 printf("%s: ", qPrintable(it.key()));
91 printArg(it.value());
92 }
93 } else if (v.userType() == qMetaTypeId<QDBusVariant>()) {
94 printArg(qvariant_cast<QDBusVariant>(v).variant());
95 } else if (v.userType() == qMetaTypeId<QDBusArgument>()) {
96 QDBusArgument arg = qvariant_cast<QDBusArgument>(v);
97 if (arg.currentSignature() == QLatin1String("av"))
98 printArg(qdbus_cast<QVariantList>(arg));
99 else if (arg.currentSignature() == QLatin1String("a{sv}"))
100 printArg(qdbus_cast<QVariantMap>(arg));
101 else
102 printf("qdbus: I don't know how to display an argument of type '%s'\n",
103 qPrintable(arg.currentSignature()));
104 } else {
105 printf("%s\n", qPrintable(v.toString()));
106 }
107}
108
109static void listObjects(const QString &service, const QString &path)
110{
111 // make a low-level call, to avoid introspecting the Introspectable interface
112 QDBusMessage call = QDBusMessage::createMethodCall(service, path.isEmpty() ? QLatin1String("/") : path,
113 QLatin1String("org.freedesktop.DBus.Introspectable"),
114 QLatin1String("Introspect"));
115 QDBusReply<QString> xml = connection.call(call);
116
117 if (path.isEmpty()) {
118 // top-level
119 if (xml.isValid()) {
120 printf("/\n");
121 } else {
122 QDBusError err = xml.error();
123 if (err.type() == QDBusError::ServiceUnknown)
124 fprintf(stderr, "Service '%s' does not exist.\n", qPrintable(service));
125 else
126 printf("Error: %s\n%s\n", qPrintable(err.name()), qPrintable(err.message()));
127 exit(2);
128 }
129 } else if (!xml.isValid()) {
130 // this is not the first object, just fail silently
131 return;
132 }
133
134 QDomDocument doc;
135 doc.setContent(xml);
136 QDomElement node = doc.documentElement();
137 QDomElement child = node.firstChildElement();
138 while (!child.isNull()) {
139 if (child.tagName() == QLatin1String("node")) {
140 QString sub = path + QLatin1Char('/') + child.attribute(QLatin1String("name"));
141 printf("%s\n", qPrintable(sub));
142 listObjects(service, sub);
143 }
144 child = child.nextSiblingElement();
145 }
146}
147
148static void listInterface(const QString &service, const QString &path, const QString &interface)
149{
150 QDBusInterface iface(service, path, interface, connection);
151 if (!iface.isValid()) {
152 QDBusError err(iface.lastError());
153 fprintf(stderr, "Interface '%s' not available in object %s at %s:\n%s (%s)\n",
154 qPrintable(interface), qPrintable(path), qPrintable(service),
155 qPrintable(err.name()), qPrintable(err.message()));
156 exit(1);
157 }
158 const QMetaObject *mo = iface.metaObject();
159
160 // properties
161 for (int i = mo->propertyOffset(); i < mo->propertyCount(); ++i) {
162 QMetaProperty mp = mo->property(i);
163 printf("property ");
164
165 if (mp.isReadable() && mp.isWritable())
166 printf("readwrite");
167 else if (mp.isReadable())
168 printf("read");
169 else
170 printf("write");
171
172 printf(" %s %s.%s\n", mp.typeName(), qPrintable(interface), mp.name());
173 }
174
175 // methods (signals and slots)
176 for (int i = mo->methodOffset(); i < mo->methodCount(); ++i) {
177 QMetaMethod mm = mo->method(i);
178
179 QByteArray signature = mm.signature();
180 signature.truncate(signature.indexOf('('));
181 printf("%s %s%s%s %s.%s(",
182 mm.methodType() == QMetaMethod::Signal ? "signal" : "method",
183 mm.tag(), *mm.tag() ? " " : "",
184 *mm.typeName() ? mm.typeName() : "void",
185 qPrintable(interface), signature.constData());
186
187 QList<QByteArray> types = mm.parameterTypes();
188 QList<QByteArray> names = mm.parameterNames();
189 bool first = true;
190 for (int i = 0; i < types.count(); ++i) {
191 printf("%s%s",
192 first ? "" : ", ",
193 types.at(i).constData());
194 if (!names.at(i).isEmpty())
195 printf(" %s", names.at(i).constData());
196 first = false;
197 }
198 printf(")\n");
199 }
200}
201
202static void listAllInterfaces(const QString &service, const QString &path)
203{
204 // make a low-level call, to avoid introspecting the Introspectable interface
205 QDBusMessage call = QDBusMessage::createMethodCall(service, path.isEmpty() ? QLatin1String("/") : path,
206 QLatin1String("org.freedesktop.DBus.Introspectable"),
207 QLatin1String("Introspect"));
208 QDBusReply<QString> xml = connection.call(call);
209
210 if (!xml.isValid()) {
211 QDBusError err = xml.error();
212 if (err.type() == QDBusError::ServiceUnknown)
213 fprintf(stderr, "Service '%s' does not exist.\n", qPrintable(service));
214 else
215 printf("Error: %s\n%s\n", qPrintable(err.name()), qPrintable(err.message()));
216 exit(2);
217 }
218
219 QDomDocument doc;
220 doc.setContent(xml);
221 QDomElement node = doc.documentElement();
222 QDomElement child = node.firstChildElement();
223 while (!child.isNull()) {
224 if (child.tagName() == QLatin1String("interface")) {
225 QString ifaceName = child.attribute(QLatin1String("name"));
226 if (QDBusUtil::isValidInterfaceName(ifaceName))
227 listInterface(service, path, ifaceName);
228 else {
229 qWarning("Invalid D-BUS interface name '%s' found while parsing introspection",
230 qPrintable(ifaceName));
231 }
232 }
233 child = child.nextSiblingElement();
234 }
235}
236
237static QStringList readList(QStringList &args)
238{
239 args.takeFirst();
240
241 QStringList retval;
242 while (!args.isEmpty() && args.at(0) != QLatin1String(")"))
243 retval += args.takeFirst();
244
245 if (args.value(0) == QLatin1String(")"))
246 args.takeFirst();
247
248 return retval;
249}
250
251static int placeCall(const QString &service, const QString &path, const QString &interface,
252 const QString &member, const QStringList& arguments, bool try_prop=true)
253{
254 QDBusInterface iface(service, path, interface, connection);
255
256 // Don't check whether the interface is valid to allow DBus try to
257 // activate the service if possible.
258
259 QList<int> knownIds;
260 bool matchFound = false;
261 QStringList args = arguments;
262 QVariantList params;
263 if (!args.isEmpty()) {
264 const QMetaObject *mo = iface.metaObject();
265 QByteArray match = member.toLatin1();
266 match += '(';
267
268 for (int i = mo->methodOffset(); i < mo->methodCount(); ++i) {
269 QMetaMethod mm = mo->method(i);
270 QByteArray signature = mm.signature();
271 if (signature.startsWith(match))
272 knownIds += i;
273 }
274
275
276 while (!matchFound) {
277 args = arguments; // reset
278 params.clear();
279 if (knownIds.isEmpty()) {
280 // Failed to set property after falling back?
281 // Bail out without displaying an error
282 if (!try_prop)
283 return 1;
284 if (try_prop && args.size() == 1) {
285 QStringList proparg;
286 proparg += interface;
287 proparg += member;
288 proparg += args.first();
289 if (!placeCall(service, path, "org.freedesktop.DBus.Properties", "Set", proparg, false))
290 return 0;
291 }
292 fprintf(stderr, "Cannot find '%s.%s' in object %s at %s\n",
293 qPrintable(interface), qPrintable(member), qPrintable(path),
294 qPrintable(service));
295 return 1;
296 }
297
298 QMetaMethod mm = mo->method(knownIds.takeFirst());
299 QList<QByteArray> types = mm.parameterTypes();
300 for (int i = 0; i < types.count(); ++i) {
301 if (types.at(i).endsWith('&')) {
302 // reference (and not a reference to const): output argument
303 // we're done with the inputs
304 while (types.count() > i)
305 types.removeLast();
306 break;
307 }
308 }
309
310 for (int i = 0; !args.isEmpty() && i < types.count(); ++i) {
311 int id = QVariant::nameToType(types.at(i));
312 if (id == QVariant::UserType)
313 id = QMetaType::type(types.at(i));
314 Q_ASSERT(id);
315
316 QVariant p;
317 QString argument;
318 if ((id == QVariant::List || id == QVariant::StringList)
319 && args.at(0) == QLatin1String("("))
320 p = readList(args);
321 else
322 p = argument = args.takeFirst();
323
324 if (id == int(QMetaType::UChar)) {
325 // special case: QVariant::convert doesn't convert to/from
326 // UChar because it can't decide if it's a character or a number
327 p = qVariantFromValue<uchar>(p.toUInt());
328 } else if (id < int(QMetaType::User) && id != int(QVariant::Map)) {
329 p.convert(QVariant::Type(id));
330 if (p.type() == QVariant::Invalid) {
331 fprintf(stderr, "Could not convert '%s' to type '%s'.\n",
332 qPrintable(argument), types.at(i).constData());
333 return 1 ;
334 }
335 } else if (id == qMetaTypeId<QDBusVariant>()) {
336 QDBusVariant tmp(p);
337 p = qVariantFromValue(tmp);
338 } else if (id == qMetaTypeId<QDBusObjectPath>()) {
339 QDBusObjectPath path(argument);
340 if (path.path().isNull()) {
341 fprintf(stderr, "Cannot pass argument '%s' because it is not a valid object path.\n",
342 qPrintable(argument));
343 return 1;
344 }
345 p = qVariantFromValue(path);
346 } else if (id == qMetaTypeId<QDBusSignature>()) {
347 QDBusSignature sig(argument);
348 if (sig.signature().isNull()) {
349 fprintf(stderr, "Cannot pass argument '%s' because it is not a valid signature.\n",
350 qPrintable(argument));
351 return 1;
352 }
353 p = qVariantFromValue(sig);
354 } else {
355 fprintf(stderr, "Sorry, can't pass arg of type '%s'.\n",
356 types.at(i).constData());
357 return 1;
358 }
359 params += p;
360 }
361 if (params.count() == types.count() && args.isEmpty())
362 matchFound = true;
363 else if (knownIds.isEmpty()) {
364 fprintf(stderr, "Invalid number of parameters\n");
365 return 1;
366 }
367 } // while (!matchFound)
368 } // if (!args.isEmpty()
369
370 QDBusMessage reply = iface.callWithArgumentList(QDBus::Block, member, params);
371 if (reply.type() == QDBusMessage::ErrorMessage) {
372 QDBusError err = reply;
373 // Failed to retrieve property after falling back?
374 // Bail out without displaying an error
375 if (!try_prop)
376 return 1;
377 if (err.type() == QDBusError::UnknownMethod && try_prop) {
378 QStringList proparg;
379 proparg += interface;
380 proparg += member;
381 if (!placeCall(service, path, "org.freedesktop.DBus.Properties", "Get", proparg, false))
382 return 0;
383 }
384 if (err.type() == QDBusError::ServiceUnknown)
385 fprintf(stderr, "Service '%s' does not exist.\n", qPrintable(service));
386 else
387 printf("Error: %s\n%s\n", qPrintable(err.name()), qPrintable(err.message()));
388 return 2;
389 } else if (reply.type() != QDBusMessage::ReplyMessage) {
390 fprintf(stderr, "Invalid reply type %d\n", int(reply.type()));
391 return 1;
392 }
393
394 foreach (QVariant v, reply.arguments())
395 printArg(v);
396
397 return 0;
398}
399
400static bool globServices(QDBusConnectionInterface *bus, const QString &glob)
401{
402 QRegExp pattern(glob, Qt::CaseSensitive, QRegExp::Wildcard);
403 if (!pattern.isValid())
404 return false;
405
406 QStringList names = bus->registeredServiceNames();
407 names.sort();
408 foreach (const QString &name, names)
409 if (pattern.exactMatch(name))
410 printf("%s\n", qPrintable(name));
411
412 return true;
413}
414
415static void printAllServices(QDBusConnectionInterface *bus)
416{
417 const QStringList services = bus->registeredServiceNames();
418 QMap<QString, QStringList> servicesWithAliases;
419
420 foreach (QString serviceName, services) {
421 QDBusReply<QString> reply = bus->serviceOwner(serviceName);
422 QString owner = reply;
423 if (owner.isEmpty())
424 owner = serviceName;
425 servicesWithAliases[owner].append(serviceName);
426 }
427
428 for (QMap<QString,QStringList>::const_iterator it = servicesWithAliases.constBegin();
429 it != servicesWithAliases.constEnd(); ++it) {
430 QStringList names = it.value();
431 names.sort();
432 printf("%s\n", qPrintable(names.join(QLatin1String("\n "))));
433 }
434}
435
436int main(int argc, char **argv)
437{
438 QCoreApplication app(argc, argv);
439 QStringList args = app.arguments();
440 args.takeFirst();
441
442 bool connectionOpened = false;
443 while (!args.isEmpty() && args.at(0).startsWith(QLatin1Char('-'))) {
444 QString arg = args.takeFirst();
445 if (arg == QLatin1String("--system")) {
446 connection = QDBusConnection::systemBus();
447 connectionOpened = true;
448 } else if (arg == QLatin1String("--literal")) {
449 printArgumentsLiterally = true;
450 } else if (arg == QLatin1String("--help")) {
451 showUsage();
452 return 0;
453 }
454 }
455
456 if (!connectionOpened)
457 connection = QDBusConnection::sessionBus();
458
459 if (!connection.isConnected()) {
460 fprintf(stderr, "Could not connect to D-Bus server: %s: %s\n",
461 qPrintable(connection.lastError().name()),
462 qPrintable(connection.lastError().message()));
463 return 1;
464 }
465
466 QDBusConnectionInterface *bus = connection.interface();
467 if (args.isEmpty()) {
468 printAllServices(bus);
469 exit(0);
470 }
471
472 QString service = args.takeFirst();
473 if (!QDBusUtil::isValidBusName(service)) {
474 if (service.contains(QLatin1Char('*'))) {
475 if (globServices(bus, service))
476 return 0;
477 }
478 fprintf(stderr, "Service '%s' is not a valid name.\n", qPrintable(service));
479 exit(1);
480 }
481
482 if (args.isEmpty()) {
483 listObjects(service, QString());
484 exit(0);
485 }
486
487 QString path = args.takeFirst();
488 if (!QDBusUtil::isValidObjectPath(path)) {
489 fprintf(stderr, "Path '%s' is not a valid path name.\n", qPrintable(path));
490 exit(1);
491 }
492 if (args.isEmpty()) {
493 listAllInterfaces(service, path);
494 exit(0);
495 }
496
497 QString interface = args.takeFirst();
498 QString member;
499 int pos = interface.lastIndexOf(QLatin1Char('.'));
500 if (pos == -1) {
501 member = interface;
502 interface.clear();
503 } else {
504 member = interface.mid(pos + 1);
505 interface.truncate(pos);
506 }
507 if (!interface.isEmpty() && !QDBusUtil::isValidInterfaceName(interface)) {
508 fprintf(stderr, "Interface '%s' is not a valid interface name.\n", qPrintable(interface));
509 exit(1);
510 }
511 if (!QDBusUtil::isValidMemberName(member)) {
512 fprintf(stderr, "Method name '%s' is not a valid member name.\n", qPrintable(member));
513 exit(1);
514 }
515
516 int ret = placeCall(service, path, interface, member, args);
517 exit(ret);
518}
519
Note: See TracBrowser for help on using the repository browser.