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

Last change on this file since 651 was 651, checked in by Dmitry A. Kuminov, 15 years ago

trunk: Merged in qt 4.6.2 sources.

File size: 17.9 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2010 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 void placeCall(const QString &service, const QString &path, const QString &interface,
252 const QString &member, QStringList args)
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 QVariantList params;
260 if (!args.isEmpty()) {
261 const QMetaObject *mo = iface.metaObject();
262 QByteArray match = member.toLatin1();
263 match += '(';
264
265 int midx = -1;
266 for (int i = mo->methodOffset(); i < mo->methodCount(); ++i) {
267 QMetaMethod mm = mo->method(i);
268 QByteArray signature = mm.signature();
269 if (signature.startsWith(match)) {
270 midx = i;
271 break;
272 }
273 }
274
275 if (midx == -1) {
276 fprintf(stderr, "Cannot find '%s.%s' in object %s at %s\n",
277 qPrintable(interface), qPrintable(member), qPrintable(path),
278 qPrintable(service));
279 exit(1);
280 }
281
282 QMetaMethod mm = mo->method(midx);
283 QList<QByteArray> types = mm.parameterTypes();
284 for (int i = 0; i < types.count(); ++i) {
285 if (types.at(i).endsWith('&')) {
286 // reference (and not a reference to const): output argument
287 // we're done with the inputs
288 while (types.count() > i)
289 types.removeLast();
290 break;
291 }
292 }
293
294 for (int i = 0; !args.isEmpty() && i < types.count(); ++i) {
295 int id = QVariant::nameToType(types.at(i));
296 if (id == QVariant::UserType)
297 id = QMetaType::type(types.at(i));
298 Q_ASSERT(id);
299
300 QVariant p;
301 QString argument;
302 if ((id == QVariant::List || id == QVariant::StringList)
303 && args.at(0) == QLatin1String("("))
304 p = readList(args);
305 else
306 p = argument = args.takeFirst();
307
308 if (id == int(QMetaType::UChar)) {
309 // special case: QVariant::convert doesn't convert to/from
310 // UChar because it can't decide if it's a character or a number
311 p = qVariantFromValue<uchar>(p.toUInt());
312 } else if (id < int(QMetaType::User) && id != int(QVariant::Map)) {
313 p.convert(QVariant::Type(id));
314 if (p.type() == QVariant::Invalid) {
315 fprintf(stderr, "Could not convert '%s' to type '%s'.\n",
316 qPrintable(argument), types.at(i).constData());
317 exit(1);
318 }
319 } else if (id == qMetaTypeId<QDBusVariant>()) {
320 QDBusVariant tmp(p);
321 p = qVariantFromValue(tmp);
322 } else if (id == qMetaTypeId<QDBusObjectPath>()) {
323 QDBusObjectPath path(argument);
324 if (path.path().isNull()) {
325 fprintf(stderr, "Cannot pass argument '%s' because it is not a valid object path.\n",
326 qPrintable(argument));
327 exit(1);
328 }
329 p = qVariantFromValue(path);
330 } else if (id == qMetaTypeId<QDBusSignature>()) {
331 QDBusSignature sig(argument);
332 if (sig.signature().isNull()) {
333 fprintf(stderr, "Cannot pass argument '%s' because it is not a valid signature.\n",
334 qPrintable(argument));
335 exit(1);
336 }
337 p = qVariantFromValue(sig);
338 } else {
339 fprintf(stderr, "Sorry, can't pass arg of type '%s'.\n",
340 types.at(i).constData());
341 exit(1);
342 }
343 params += p;
344 }
345 if (params.count() != types.count() || !args.isEmpty()) {
346 fprintf(stderr, "Invalid number of parameters\n");
347 exit(1);
348 }
349 }
350
351 QDBusMessage reply = iface.callWithArgumentList(QDBus::Block, member, params);
352 if (reply.type() == QDBusMessage::ErrorMessage) {
353 QDBusError err = reply;
354 if (err.type() == QDBusError::ServiceUnknown)
355 fprintf(stderr, "Service '%s' does not exist.\n", qPrintable(service));
356 else
357 printf("Error: %s\n%s\n", qPrintable(err.name()), qPrintable(err.message()));
358 exit(2);
359 } else if (reply.type() != QDBusMessage::ReplyMessage) {
360 fprintf(stderr, "Invalid reply type %d\n", int(reply.type()));
361 exit(1);
362 }
363
364 foreach (QVariant v, reply.arguments())
365 printArg(v);
366
367 exit(0);
368}
369
370static bool globServices(QDBusConnectionInterface *bus, const QString &glob)
371{
372 QRegExp pattern(glob, Qt::CaseSensitive, QRegExp::Wildcard);
373 if (!pattern.isValid())
374 return false;
375
376 QStringList names = bus->registeredServiceNames();
377 names.sort();
378 foreach (const QString &name, names)
379 if (pattern.exactMatch(name))
380 printf("%s\n", qPrintable(name));
381
382 return true;
383}
384
385static void printAllServices(QDBusConnectionInterface *bus)
386{
387 const QStringList services = bus->registeredServiceNames();
388 QMap<QString, QStringList> servicesWithAliases;
389
390 foreach (QString serviceName, services) {
391 QDBusReply<QString> reply = bus->serviceOwner(serviceName);
392 QString owner = reply;
393 if (owner.isEmpty())
394 owner = serviceName;
395 servicesWithAliases[owner].append(serviceName);
396 }
397
398 for (QMap<QString,QStringList>::const_iterator it = servicesWithAliases.constBegin();
399 it != servicesWithAliases.constEnd(); ++it) {
400 QStringList names = it.value();
401 names.sort();
402 printf("%s\n", qPrintable(names.join(QLatin1String("\n "))));
403 }
404}
405
406int main(int argc, char **argv)
407{
408 QCoreApplication app(argc, argv);
409 QStringList args = app.arguments();
410 args.takeFirst();
411
412 bool connectionOpened = false;
413 while (!args.isEmpty() && args.at(0).startsWith(QLatin1Char('-'))) {
414 QString arg = args.takeFirst();
415 if (arg == QLatin1String("--system")) {
416 connection = QDBusConnection::systemBus();
417 connectionOpened = true;
418 } else if (arg == QLatin1String("--literal")) {
419 printArgumentsLiterally = true;
420 } else if (arg == QLatin1String("--help")) {
421 showUsage();
422 return 0;
423 }
424 }
425
426 if (!connectionOpened)
427 connection = QDBusConnection::sessionBus();
428
429 if (!connection.isConnected()) {
430 fprintf(stderr, "Could not connect to D-Bus server: %s: %s\n",
431 qPrintable(connection.lastError().name()),
432 qPrintable(connection.lastError().message()));
433 return 1;
434 }
435
436 QDBusConnectionInterface *bus = connection.interface();
437 if (args.isEmpty()) {
438 printAllServices(bus);
439 exit(0);
440 }
441
442 QString service = args.takeFirst();
443 if (!QDBusUtil::isValidBusName(service)) {
444 if (service.contains(QLatin1Char('*'))) {
445 if (globServices(bus, service))
446 return 0;
447 }
448 fprintf(stderr, "Service '%s' is not a valid name.\n", qPrintable(service));
449 exit(1);
450 }
451
452 if (args.isEmpty()) {
453 listObjects(service, QString());
454 exit(0);
455 }
456
457 QString path = args.takeFirst();
458 if (!QDBusUtil::isValidObjectPath(path)) {
459 fprintf(stderr, "Path '%s' is not a valid path name.\n", qPrintable(path));
460 exit(1);
461 }
462 if (args.isEmpty()) {
463 listAllInterfaces(service, path);
464 exit(0);
465 }
466
467 QString interface = args.takeFirst();
468 QString member;
469 int pos = interface.lastIndexOf(QLatin1Char('.'));
470 if (pos == -1) {
471 member = interface;
472 interface.clear();
473 } else {
474 member = interface.mid(pos + 1);
475 interface.truncate(pos);
476 }
477 if (!interface.isEmpty() && !QDBusUtil::isValidInterfaceName(interface)) {
478 fprintf(stderr, "Interface '%s' is not a valid interface name.\n", qPrintable(interface));
479 exit(1);
480 }
481 if (!QDBusUtil::isValidMemberName(member)) {
482 fprintf(stderr, "Method name '%s' is not a valid member name.\n", qPrintable(member));
483 exit(1);
484 }
485
486 placeCall(service, path, interface, member, args);
487}
488
Note: See TracBrowser for help on using the repository browser.