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

Last change on this file since 5 was 2, checked in by Dmitry A. Kuminov, 16 years ago

Initially imported qt-all-opensource-src-4.5.1 from Trolltech.

File size: 17.5 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4** Contact: Qt Software Information (qt-info@nokia.com)
5**
6** This file is part of the tools applications of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial Usage
10** Licensees holding valid Qt Commercial licenses may use this file in
11** accordance with the Qt Commercial License Agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and Nokia.
14**
15** GNU Lesser General Public License Usage
16** Alternatively, this file may be used under the terms of the GNU Lesser
17** General Public License version 2.1 as published by the Free Software
18** Foundation and appearing in the file LICENSE.LGPL included in the
19** packaging of this file. Please review the following information to
20** ensure the GNU Lesser General Public License version 2.1 requirements
21** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
22**
23** In addition, as a special exception, Nokia gives you certain
24** additional rights. These rights are described in the Nokia Qt LGPL
25** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
26** 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 are unsure which license is appropriate for your use, please
37** contact the sales department at qt-sales@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 QDBusInterface iface(service, path.isEmpty() ? QLatin1String("/") : path,
112 QLatin1String("org.freedesktop.DBus.Introspectable"), connection);
113 if (!iface.isValid()) {
114 QDBusError err(iface.lastError());
115 fprintf(stderr, "Cannot introspect object %s at %s:\n%s (%s)\n",
116 qPrintable(path.isEmpty() ? QString(QLatin1String("/")) : path), qPrintable(service),
117 qPrintable(err.name()), qPrintable(err.message()));
118 exit(1);
119 }
120 QDBusReply<QString> xml = iface.call(QLatin1String("Introspect"));
121
122 if (!xml.isValid())
123 return; // silently
124
125 QDomDocument doc;
126 doc.setContent(xml);
127 QDomElement node = doc.documentElement();
128 QDomElement child = node.firstChildElement();
129 while (!child.isNull()) {
130 if (child.tagName() == QLatin1String("node")) {
131 QString sub = path + QLatin1Char('/') + child.attribute(QLatin1String("name"));
132 printf("%s\n", qPrintable(sub));
133 listObjects(service, sub);
134 }
135 child = child.nextSiblingElement();
136 }
137}
138
139static void listInterface(const QString &service, const QString &path, const QString &interface)
140{
141 QDBusInterface iface(service, path, interface, connection);
142 if (!iface.isValid()) {
143 QDBusError err(iface.lastError());
144 fprintf(stderr, "Interface '%s' not available in object %s at %s:\n%s (%s)\n",
145 qPrintable(interface), qPrintable(path), qPrintable(service),
146 qPrintable(err.name()), qPrintable(err.message()));
147 exit(1);
148 }
149 const QMetaObject *mo = iface.metaObject();
150
151 // properties
152 for (int i = mo->propertyOffset(); i < mo->propertyCount(); ++i) {
153 QMetaProperty mp = mo->property(i);
154 printf("property ");
155
156 if (mp.isReadable() && mp.isWritable())
157 printf("readwrite");
158 else if (mp.isReadable())
159 printf("read");
160 else
161 printf("write");
162
163 printf(" %s %s.%s\n", mp.typeName(), qPrintable(interface), mp.name());
164 }
165
166 // methods (signals and slots)
167 for (int i = mo->methodOffset(); i < mo->methodCount(); ++i) {
168 QMetaMethod mm = mo->method(i);
169
170 QByteArray signature = mm.signature();
171 signature.truncate(signature.indexOf('('));
172 printf("%s %s%s%s %s.%s(",
173 mm.methodType() == QMetaMethod::Signal ? "signal" : "method",
174 mm.tag(), *mm.tag() ? " " : "",
175 *mm.typeName() ? mm.typeName() : "void",
176 qPrintable(interface), signature.constData());
177
178 QList<QByteArray> types = mm.parameterTypes();
179 QList<QByteArray> names = mm.parameterNames();
180 bool first = true;
181 for (int i = 0; i < types.count(); ++i) {
182 printf("%s%s",
183 first ? "" : ", ",
184 types.at(i).constData());
185 if (!names.at(i).isEmpty())
186 printf(" %s", names.at(i).constData());
187 first = false;
188 }
189 printf(")\n");
190 }
191}
192
193static void listAllInterfaces(const QString &service, const QString &path)
194{
195 QDBusInterface iface(service, path, QLatin1String("org.freedesktop.DBus.Introspectable"), connection);
196 if (!iface.isValid()) {
197 QDBusError err(iface.lastError());
198 fprintf(stderr, "Cannot introspect object %s at %s:\n%s (%s)\n",
199 qPrintable(path), qPrintable(service),
200 qPrintable(err.name()), qPrintable(err.message()));
201 exit(1);
202 }
203 QDBusReply<QString> xml = iface.call(QLatin1String("Introspect"));
204
205 if (!xml.isValid())
206 return; // silently
207
208 QDomDocument doc;
209 doc.setContent(xml);
210 QDomElement node = doc.documentElement();
211 QDomElement child = node.firstChildElement();
212 while (!child.isNull()) {
213 if (child.tagName() == QLatin1String("interface")) {
214 QString ifaceName = child.attribute(QLatin1String("name"));
215 if (QDBusUtil::isValidInterfaceName(ifaceName))
216 listInterface(service, path, ifaceName);
217 else {
218 qWarning("Invalid D-BUS interface name '%s' found while parsing introspection",
219 qPrintable(ifaceName));
220 }
221 }
222 child = child.nextSiblingElement();
223 }
224}
225
226static QStringList readList(QStringList &args)
227{
228 args.takeFirst();
229
230 QStringList retval;
231 while (!args.isEmpty() && args.at(0) != QLatin1String(")"))
232 retval += args.takeFirst();
233
234 if (args.value(0) == QLatin1String(")"))
235 args.takeFirst();
236
237 return retval;
238}
239
240static void placeCall(const QString &service, const QString &path, const QString &interface,
241 const QString &member, QStringList args)
242{
243 QDBusInterface iface(service, path, interface, connection);
244 if (!iface.isValid()) {
245 QDBusError err(iface.lastError());
246 fprintf(stderr, "Interface '%s' not available in object %s at %s:\n%s (%s)\n",
247 qPrintable(interface), qPrintable(path), qPrintable(service),
248 qPrintable(err.name()), qPrintable(err.message()));
249 exit(1);
250 }
251
252 QVariantList params;
253 if (!args.isEmpty()) {
254 const QMetaObject *mo = iface.metaObject();
255 QByteArray match = member.toLatin1();
256 match += '(';
257
258 int midx = -1;
259 for (int i = mo->methodOffset(); i < mo->methodCount(); ++i) {
260 QMetaMethod mm = mo->method(i);
261 QByteArray signature = mm.signature();
262 if (signature.startsWith(match)) {
263 midx = i;
264 break;
265 }
266 }
267
268 if (midx == -1) {
269 fprintf(stderr, "Cannot find '%s.%s' in object %s at %s\n",
270 qPrintable(interface), qPrintable(member), qPrintable(path),
271 qPrintable(service));
272 exit(1);
273 }
274
275 QMetaMethod mm = mo->method(midx);
276 QList<QByteArray> types = mm.parameterTypes();
277 for (int i = 0; i < types.count(); ++i) {
278 if (types.at(i).endsWith('&')) {
279 // reference (and not a reference to const): output argument
280 // we're done with the inputs
281 while (types.count() > i)
282 types.removeLast();
283 break;
284 }
285 }
286
287 for (int i = 0; !args.isEmpty() && i < types.count(); ++i) {
288 int id = QVariant::nameToType(types.at(i));
289 if (id == QVariant::UserType)
290 id = QMetaType::type(types.at(i));
291 Q_ASSERT(id);
292
293 QVariant p;
294 QString argument;
295 if ((id == QVariant::List || id == QVariant::StringList)
296 && args.at(0) == QLatin1String("("))
297 p = readList(args);
298 else
299 p = argument = args.takeFirst();
300
301 if (id == int(QMetaType::UChar)) {
302 // special case: QVariant::convert doesn't convert to/from
303 // UChar because it can't decide if it's a character or a number
304 p = qVariantFromValue<uchar>(p.toUInt());
305 } else if (id < int(QMetaType::User) && id != int(QVariant::Map)) {
306 p.convert(QVariant::Type(id));
307 if (p.type() == QVariant::Invalid) {
308 fprintf(stderr, "Could not convert '%s' to type '%s'.\n",
309 qPrintable(argument), types.at(i).constData());
310 exit(1);
311 }
312 } else if (id == qMetaTypeId<QDBusVariant>()) {
313 QDBusVariant tmp(p);
314 p = qVariantFromValue(tmp);
315 } else if (id == qMetaTypeId<QDBusObjectPath>()) {
316 QDBusObjectPath path(argument);
317 if (path.path().isNull()) {
318 fprintf(stderr, "Cannot pass argument '%s' because it is not a valid object path.\n",
319 qPrintable(argument));
320 exit(1);
321 }
322 p = qVariantFromValue(path);
323 } else if (id == qMetaTypeId<QDBusSignature>()) {
324 QDBusSignature sig(argument);
325 if (sig.signature().isNull()) {
326 fprintf(stderr, "Cannot pass argument '%s' because it is not a valid signature.\n",
327 qPrintable(argument));
328 exit(1);
329 }
330 p = qVariantFromValue(sig);
331 } else {
332 fprintf(stderr, "Sorry, can't pass arg of type '%s'.\n",
333 types.at(i).constData());
334 exit(1);
335 }
336 params += p;
337 }
338 if (params.count() != types.count() || !args.isEmpty()) {
339 fprintf(stderr, "Invalid number of parameters\n");
340 exit(1);
341 }
342 }
343
344 QDBusMessage reply = iface.callWithArgumentList(QDBus::Block, member, params);
345 if (reply.type() == QDBusMessage::ErrorMessage) {
346 QDBusError err = reply;
347 printf("Error: %s\n%s\n", qPrintable(err.name()), qPrintable(err.message()));
348 exit(2);
349 } else if (reply.type() != QDBusMessage::ReplyMessage) {
350 fprintf(stderr, "Invalid reply type %d\n", int(reply.type()));
351 exit(1);
352 }
353
354 foreach (QVariant v, reply.arguments())
355 printArg(v);
356
357 exit(0);
358}
359
360static bool globServices(QDBusConnectionInterface *bus, const QString &glob)
361{
362 QRegExp pattern(glob, Qt::CaseSensitive, QRegExp::Wildcard);
363 if (!pattern.isValid())
364 return false;
365
366 QStringList names = bus->registeredServiceNames();
367 names.sort();
368 foreach (const QString &name, names)
369 if (pattern.exactMatch(name))
370 printf("%s\n", qPrintable(name));
371
372 return true;
373}
374
375static void printAllServices(QDBusConnectionInterface *bus)
376{
377 const QStringList services = bus->registeredServiceNames();
378 QMap<QString, QStringList> servicesWithAliases;
379
380 foreach (QString serviceName, services) {
381 QDBusReply<QString> reply = bus->serviceOwner(serviceName);
382 QString owner = reply;
383 if (owner.isEmpty())
384 owner = serviceName;
385 servicesWithAliases[owner].append(serviceName);
386 }
387
388 for (QMap<QString,QStringList>::const_iterator it = servicesWithAliases.constBegin();
389 it != servicesWithAliases.constEnd(); ++it) {
390 QStringList names = it.value();
391 names.sort();
392 printf("%s\n", qPrintable(names.join(QLatin1String("\n "))));
393 }
394}
395
396int main(int argc, char **argv)
397{
398 QCoreApplication app(argc, argv);
399 QStringList args = app.arguments();
400 args.takeFirst();
401
402 bool connectionOpened = false;
403 while (!args.isEmpty() && args.at(0).startsWith(QLatin1Char('-'))) {
404 QString arg = args.takeFirst();
405 if (arg == QLatin1String("--system")) {
406 connection = QDBusConnection::systemBus();
407 connectionOpened = true;
408 } else if (arg == QLatin1String("--literal")) {
409 printArgumentsLiterally = true;
410 } else if (arg == QLatin1String("--help")) {
411 showUsage();
412 return 0;
413 }
414 }
415
416 if (!connectionOpened)
417 connection = QDBusConnection::sessionBus();
418
419 if (!connection.isConnected()) {
420 fprintf(stderr, "Could not connect to D-Bus server: %s: %s\n",
421 qPrintable(connection.lastError().name()),
422 qPrintable(connection.lastError().message()));
423 return 1;
424 }
425
426 QDBusConnectionInterface *bus = connection.interface();
427 if (args.isEmpty()) {
428 printAllServices(bus);
429 exit(0);
430 }
431
432 QString service = args.takeFirst();
433 if (!QDBusUtil::isValidBusName(service)) {
434 if (service.contains(QLatin1Char('*'))) {
435 if (globServices(bus, service))
436 return 0;
437 }
438 fprintf(stderr, "Service '%s' is not a valid name.\n", qPrintable(service));
439 exit(1);
440 }
441 if (!bus->isServiceRegistered(service)) {
442 fprintf(stderr, "Service '%s' does not exist.\n", qPrintable(service));
443 exit(1);
444 }
445
446 if (args.isEmpty()) {
447 printf("/\n");
448 listObjects(service, QString());
449 exit(0);
450 }
451
452 QString path = args.takeFirst();
453 if (!QDBusUtil::isValidObjectPath(path)) {
454 fprintf(stderr, "Path '%s' is not a valid path name.\n", qPrintable(path));
455 exit(1);
456 }
457 if (args.isEmpty()) {
458 listAllInterfaces(service, path);
459 exit(0);
460 }
461
462 QString interface = args.takeFirst();
463 QString member;
464 int pos = interface.lastIndexOf(QLatin1Char('.'));
465 if (pos == -1) {
466 member = interface;
467 interface.clear();
468 } else {
469 member = interface.mid(pos + 1);
470 interface.truncate(pos);
471 }
472 if (!interface.isEmpty() && !QDBusUtil::isValidInterfaceName(interface)) {
473 fprintf(stderr, "Interface '%s' is not a valid interface name.\n", qPrintable(interface));
474 exit(1);
475 }
476 if (!QDBusUtil::isValidMemberName(member)) {
477 fprintf(stderr, "Method name '%s' is not a valid member name.\n", qPrintable(member));
478 exit(1);
479 }
480
481 placeCall(service, path, interface, member, args);
482}
483
Note: See TracBrowser for help on using the repository browser.