From d761c6278305ef8737daca4bc3e61a119b40e107 Mon Sep 17 00:00:00 2001 From: David Edmundson Date: Fri, 6 Apr 2018 01:27:32 +0100 Subject: QDBusServiceWatcher namespace prefix support This allows a user to efficiently watch for services with a common domain prefix. This is exposed in the API via a wildcard character in the service name. For example creating a watcher on "org.mpris*" will match "org.mpris.foo" "org.mpris.bar" and "org.mpris" itself. It will not match org.mprisasdf. Internally the argument match rules have been expanded from a single QStringList to a struct containing args and arg0namespace. This was done so that we can easily use argpath in match rules in the future. Change-Id: I55882ab603cc6ba478e8c0ea9a6800f6e483a50c Reviewed-by: Kai Uwe Broulik Reviewed-by: Thiago Macieira Reviewed-by: David Faure --- src/dbus/qdbusintegrator.cpp | 89 ++++++++++++++++++++++++++++++++------------ 1 file changed, 65 insertions(+), 24 deletions(-) (limited to 'src/dbus/qdbusintegrator.cpp') diff --git a/src/dbus/qdbusintegrator.cpp b/src/dbus/qdbusintegrator.cpp index 03de5b0091..cee5c821c8 100644 --- a/src/dbus/qdbusintegrator.cpp +++ b/src/dbus/qdbusintegrator.cpp @@ -338,7 +338,7 @@ void QDBusConnectionPrivate::_q_newConnection(QDBusConnectionPrivate *newConnect static QByteArray buildMatchRule(const QString &service, const QString &objectPath, const QString &interface, - const QString &member, const QStringList &argMatch, const QString & /*signature*/) + const QString &member, const QDBusConnectionPrivate::ArgMatchRules &argMatch, const QString & /*signature*/) { QString result = QLatin1String("type='signal',"); QString keyValue = QLatin1String("%1='%2',"); @@ -353,11 +353,14 @@ static QByteArray buildMatchRule(const QString &service, result += keyValue.arg(QLatin1String("member"), member); // add the argument string-matching now - if (!argMatch.isEmpty()) { + if (!argMatch.args.isEmpty()) { keyValue = QLatin1String("arg%1='%2',"); - for (int i = 0; i < argMatch.count(); ++i) - if (!argMatch.at(i).isNull()) - result += keyValue.arg(i).arg(argMatch.at(i)); + for (int i = 0; i < argMatch.args.count(); ++i) + if (!argMatch.args.at(i).isNull()) + result += keyValue.arg(i).arg(argMatch.args.at(i)); + } + if (!argMatch.arg0namespace.isEmpty()) { + result += QStringLiteral("arg0namespace='%1',").arg(argMatch.arg0namespace); } result.chop(1); // remove ending comma @@ -456,21 +459,26 @@ static QObject *findChildObject(const QDBusConnectionPrivate::ObjectTreeNode *ro return 0; } -static QStringList matchArgsForService(const QString &service, QDBusServiceWatcher::WatchMode mode) +static QDBusConnectionPrivate::ArgMatchRules matchArgsForService(const QString &service, QDBusServiceWatcher::WatchMode mode) { - QStringList matchArgs; - matchArgs << service; + QDBusConnectionPrivate::ArgMatchRules matchArgs; + if (service.endsWith(QLatin1Char('*'))) { + matchArgs.arg0namespace = service.chopped(1); + matchArgs.args << QString(); + } + else + matchArgs.args << service; switch (mode) { case QDBusServiceWatcher::WatchForOwnerChange: break; case QDBusServiceWatcher::WatchForRegistration: - matchArgs << QString::fromLatin1("", 0); + matchArgs.args << QString::fromLatin1("", 0); break; case QDBusServiceWatcher::WatchForUnregistration: - matchArgs << QString() << QString::fromLatin1("", 0); + matchArgs.args << QString() << QString::fromLatin1("", 0); break; } return matchArgs; @@ -1310,7 +1318,7 @@ int QDBusConnectionPrivate::findSlot(QObject* obj, const QByteArray &normalizedN bool QDBusConnectionPrivate::prepareHook(QDBusConnectionPrivate::SignalHook &hook, QString &key, const QString &service, const QString &path, const QString &interface, const QString &name, - const QStringList &argMatch, + const ArgMatchRules &argMatch, QObject *receiver, const char *signal, int minMIdx, bool buildSignature) { @@ -1620,14 +1628,14 @@ void QDBusConnectionPrivate::handleSignal(const QString &key, const QDBusMessage continue; if (hook.signature.isEmpty() && !hook.signature.isNull() && !msg.signature().isEmpty()) continue; - if (!hook.argumentMatch.isEmpty()) { + if (!hook.argumentMatch.args.isEmpty()) { const QVariantList arguments = msg.arguments(); - if (hook.argumentMatch.size() > arguments.size()) + if (hook.argumentMatch.args.size() > arguments.size()) continue; bool matched = true; - for (int i = 0; i < hook.argumentMatch.size(); ++i) { - const QString ¶m = hook.argumentMatch.at(i); + for (int i = 0; i < hook.argumentMatch.args.size(); ++i) { + const QString ¶m = hook.argumentMatch.args.at(i); if (param.isNull()) continue; // don't try to match against this if (param == arguments.at(i).toString()) @@ -1638,7 +1646,15 @@ void QDBusConnectionPrivate::handleSignal(const QString &key, const QDBusMessage if (!matched) continue; } - + if (!hook.argumentMatch.arg0namespace.isEmpty()) { + const QVariantList arguments = msg.arguments(); + if (arguments.size() < 1) + continue; + const QString param = arguments.at(0).toString(); + if (param != hook.argumentMatch.arg0namespace + && !param.startsWith(hook.argumentMatch.arg0namespace + QLatin1Char('.'))) + continue; + } activateSignal(hook, msg); } } @@ -2180,10 +2196,21 @@ void QDBusConnectionPrivate::sendInternal(QDBusPendingCallPrivate *pcall, void * } } + bool QDBusConnectionPrivate::connectSignal(const QString &service, const QString &path, const QString &interface, const QString &name, const QStringList &argumentMatch, const QString &signature, QObject *receiver, const char *slot) +{ + ArgMatchRules rules; + rules.args = argumentMatch; + return connectSignal(service, path, interface, name, rules, signature, receiver, slot); +} + +bool QDBusConnectionPrivate::connectSignal(const QString &service, + const QString &path, const QString &interface, const QString &name, + const ArgMatchRules &argumentMatch, const QString &signature, + QObject *receiver, const char *slot) { // check the slot QDBusConnectionPrivate::SignalHook hook; @@ -2241,9 +2268,11 @@ bool QDBusConnectionPrivate::addSignalHook(const QString &key, const SignalHook WatchedServicesHash::mapped_type &data = watchedServices[hook.service]; if (++data.refcount == 1) { // we need to watch for this service changing + ArgMatchRules rules; + rules.args << hook.service; q_dbus_bus_add_match(connection, buildMatchRule(QDBusUtil::dbusService(), QString(), QDBusUtil::dbusInterface(), - QDBusUtil::nameOwnerChanged(), QStringList() << hook.service, QString()), + QDBusUtil::nameOwnerChanged(), rules, QString()), NULL); data.owner = getNameOwnerNoCache(hook.service); qDBusDebug() << this << "Watching service" << hook.service << "for owner changes (current owner:" @@ -2255,9 +2284,19 @@ bool QDBusConnectionPrivate::addSignalHook(const QString &key, const SignalHook return true; } +bool QDBusConnectionPrivate::disconnectSignal(const QString &service, + const QString &path, const QString &interface, const QString &name, + const QStringList &argumentMatch, const QString &signature, + QObject *receiver, const char *slot) +{ + ArgMatchRules rules; + rules.args = argumentMatch; + return disconnectSignal(service, path, interface, name, rules, signature, receiver, slot); +} + bool QDBusConnectionPrivate::disconnectSignal(const QString &service, const QString &path, const QString &interface, const QString &name, - const QStringList &argumentMatch, const QString &signature, + const ArgMatchRules &argumentMatch, const QString &signature, QObject *receiver, const char *slot) { // check the slot @@ -2288,7 +2327,7 @@ bool QDBusConnectionPrivate::removeSignalHook(const QString &key, const SignalHo entry.signature == hook.signature && entry.obj == hook.obj && entry.midx == hook.midx && - entry.argumentMatch == hook.argumentMatch) { + entry.argumentMatch.args == hook.argumentMatch.args) { // no need to compare the parameters if it's the same slot removeSignalHookNoLock(it); return true; // it was there @@ -2330,9 +2369,11 @@ QDBusConnectionPrivate::removeSignalHookNoLock(SignalHookHash::Iterator it) if (sit != watchedServices.end()) { if (--sit.value().refcount == 0) { watchedServices.erase(sit); + ArgMatchRules rules; + rules.args << hook.service; q_dbus_bus_remove_match(connection, buildMatchRule(QDBusUtil::dbusService(), QString(), QDBusUtil::dbusInterface(), - QDBusUtil::nameOwnerChanged(), QStringList() << hook.service, QString()), + QDBusUtil::nameOwnerChanged(), rules, QString()), NULL); } } @@ -2393,7 +2434,7 @@ void QDBusConnectionPrivate::connectRelay(const QString &service, QByteArray sig; sig.append(QSIGNAL_CODE + '0'); sig.append(signal.methodSignature()); - if (!prepareHook(hook, key, service, path, interface, QString(), QStringList(), receiver, sig, + if (!prepareHook(hook, key, service, path, interface, QString(), ArgMatchRules(), receiver, sig, QDBusAbstractInterface::staticMetaObject.methodCount(), true)) return; // don't connect @@ -2414,7 +2455,7 @@ void QDBusConnectionPrivate::disconnectRelay(const QString &service, QByteArray sig; sig.append(QSIGNAL_CODE + '0'); sig.append(signal.methodSignature()); - if (!prepareHook(hook, key, service, path, interface, QString(), QStringList(), receiver, sig, + if (!prepareHook(hook, key, service, path, interface, QString(), ArgMatchRules(), receiver, sig, QDBusAbstractInterface::staticMetaObject.methodCount(), true)) return; // don't disconnect @@ -2447,7 +2488,7 @@ bool QDBusConnectionPrivate::shouldWatchService(const QString &service) */ void QDBusConnectionPrivate::watchService(const QString &service, QDBusServiceWatcher::WatchMode mode, QObject *obj, const char *member) { - QStringList matchArgs = matchArgsForService(service, mode); + ArgMatchRules matchArgs = matchArgsForService(service, mode); connectSignal(QDBusUtil::dbusService(), QString(), QDBusUtil::dbusInterface(), QDBusUtil::nameOwnerChanged(), matchArgs, QString(), obj, member); } @@ -2462,7 +2503,7 @@ void QDBusConnectionPrivate::watchService(const QString &service, QDBusServiceWa */ void QDBusConnectionPrivate::unwatchService(const QString &service, QDBusServiceWatcher::WatchMode mode, QObject *obj, const char *member) { - QStringList matchArgs = matchArgsForService(service, mode); + ArgMatchRules matchArgs = matchArgsForService(service, mode); disconnectSignal(QDBusUtil::dbusService(), QString(), QDBusUtil::dbusInterface(), QDBusUtil::nameOwnerChanged(), matchArgs, QString(), obj, member); } -- cgit v1.2.3