source: branches/vendor/trolltech/qt/current/tools/qdbus/qdbus/qdbus.cpp @ 2

Last change on this file since 2 was 2, checked in by Dmitry A. Kuminov, 12 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.