src/dbus/qdbusintegrator.cpp
changeset 3 41300fa6a67c
parent 0 1918ee327afb
child 4 3b1da2848fc7
equal deleted inserted replaced
2:56cd8111b7f7 3:41300fa6a67c
   390 
   390 
   391 } // extern "C"
   391 } // extern "C"
   392 
   392 
   393 static QByteArray buildMatchRule(const QString &service, const QString & /*owner*/,
   393 static QByteArray buildMatchRule(const QString &service, const QString & /*owner*/,
   394                                  const QString &objectPath, const QString &interface,
   394                                  const QString &objectPath, const QString &interface,
   395                                  const QString &member, const QString & /*signature*/)
   395                                  const QString &member, const QStringList &argMatch, const QString & /*signature*/)
   396 {
   396 {
   397     QString result = QLatin1String("type='signal',");
   397     QString result = QLatin1String("type='signal',");
   398     QString keyValue = QLatin1String("%1='%2',");
   398     QString keyValue = QLatin1String("%1='%2',");
   399 
   399 
   400     if (!service.isEmpty())
   400     if (!service.isEmpty())
   403         result += keyValue.arg(QLatin1String("path"), objectPath);
   403         result += keyValue.arg(QLatin1String("path"), objectPath);
   404     if (!interface.isEmpty())
   404     if (!interface.isEmpty())
   405         result += keyValue.arg(QLatin1String("interface"), interface);
   405         result += keyValue.arg(QLatin1String("interface"), interface);
   406     if (!member.isEmpty())
   406     if (!member.isEmpty())
   407         result += keyValue.arg(QLatin1String("member"), member);
   407         result += keyValue.arg(QLatin1String("member"), member);
       
   408 
       
   409     // add the argument string-matching now
       
   410     if (!argMatch.isEmpty()) {
       
   411         keyValue = QLatin1String("arg%1='%2',");
       
   412         for (int i = 0; i < argMatch.count(); ++i)
       
   413             if (!argMatch.at(i).isNull())
       
   414                 result += keyValue.arg(i).arg(argMatch.at(i));
       
   415     }
   408 
   416 
   409     result.chop(1);             // remove ending comma
   417     result.chop(1);             // remove ending comma
   410     return result.toLatin1();
   418     return result.toLatin1();
   411 }
   419 }
   412 
   420 
   489         }
   497         }
   490     }
   498     }
   491 
   499 
   492     // object not found
   500     // object not found
   493     return 0;
   501     return 0;
       
   502 }
       
   503 
       
   504 static bool shouldWatchService(const QString &service)
       
   505 {
       
   506     return !service.isEmpty() && !service.startsWith(QLatin1Char(':'));
   494 }
   507 }
   495 
   508 
   496 extern QDBUS_EXPORT void qDBusAddSpyHook(QDBusSpyHook);
   509 extern QDBUS_EXPORT void qDBusAddSpyHook(QDBusSpyHook);
   497 void qDBusAddSpyHook(QDBusSpyHook hook)
   510 void qDBusAddSpyHook(QDBusSpyHook hook)
   498 {
   511 {
   919     : QObject(p), ref(1), mode(InvalidMode), connection(0), server(0), busService(0),
   932     : QObject(p), ref(1), mode(InvalidMode), connection(0), server(0), busService(0),
   920       watchAndTimeoutLock(QMutex::Recursive),
   933       watchAndTimeoutLock(QMutex::Recursive),
   921       rootNode(QString(QLatin1Char('/')))
   934       rootNode(QString(QLatin1Char('/')))
   922 {
   935 {
   923     static const bool threads = q_dbus_threads_init_default();
   936     static const bool threads = q_dbus_threads_init_default();
   924     static const int debugging = ::isDebugging = qgetenv("QDBUS_DEBUG").toInt();
   937     static const int debugging = qgetenv("QDBUS_DEBUG").toInt();
       
   938     ::isDebugging = debugging;
   925     Q_UNUSED(threads)
   939     Q_UNUSED(threads)
   926     Q_UNUSED(debugging)
   940     Q_UNUSED(debugging)
   927 
   941 
   928 #ifdef QDBUS_THREAD_DEBUG
   942 #ifdef QDBUS_THREAD_DEBUG
   929     if (debugging > 1)
   943     if (debugging > 1)
   931 #endif
   945 #endif
   932 
   946 
   933     QDBusMetaTypeId::init();
   947     QDBusMetaTypeId::init();
   934 
   948 
   935     rootNode.flags = 0;
   949     rootNode.flags = 0;
   936 
   950     watchedServiceNames[QLatin1String(DBUS_SERVICE_DBUS)] = 1;
   937     connect(this, SIGNAL(serviceOwnerChanged(QString,QString,QString)),
       
   938             this, SLOT(_q_serviceOwnerChanged(QString,QString,QString)));
       
   939 }
   951 }
   940 
   952 
   941 QDBusConnectionPrivate::~QDBusConnectionPrivate()
   953 QDBusConnectionPrivate::~QDBusConnectionPrivate()
   942 {
   954 {
   943     if (thread() && thread() != QThread::currentThread())
   955     if (thread() && thread() != QThread::currentThread())
  1163 }
  1175 }
  1164 
  1176 
  1165 void QDBusConnectionPrivate::_q_serviceOwnerChanged(const QString &name,
  1177 void QDBusConnectionPrivate::_q_serviceOwnerChanged(const QString &name,
  1166                                                     const QString &oldOwner, const QString &newOwner)
  1178                                                     const QString &oldOwner, const QString &newOwner)
  1167 {
  1179 {
  1168     if (oldOwner == baseService)
  1180     Q_UNUSED(oldOwner);
  1169         unregisterService(name);
       
  1170     if (newOwner == baseService)
       
  1171         registerService(name);
       
  1172 
       
  1173     QDBusWriteLocker locker(UpdateSignalHookOwnerAction, this);
  1181     QDBusWriteLocker locker(UpdateSignalHookOwnerAction, this);
  1174     QMutableHashIterator<QString, SignalHook> it(signalHooks);
  1182     QMutableHashIterator<QString, SignalHook> it(signalHooks);
  1175     it.toFront();
  1183     it.toFront();
  1176     while (it.hasNext())
  1184     while (it.hasNext())
  1177         if (it.next().value().service == name)
  1185         if (it.next().value().service == name)
  1193 }
  1201 }
  1194 
  1202 
  1195 bool QDBusConnectionPrivate::prepareHook(QDBusConnectionPrivate::SignalHook &hook, QString &key,
  1203 bool QDBusConnectionPrivate::prepareHook(QDBusConnectionPrivate::SignalHook &hook, QString &key,
  1196                                          const QString &service, const QString &owner,
  1204                                          const QString &service, const QString &owner,
  1197                                          const QString &path, const QString &interface, const QString &name,
  1205                                          const QString &path, const QString &interface, const QString &name,
       
  1206                                          const QStringList &argMatch,
  1198                                          QObject *receiver, const char *signal, int minMIdx,
  1207                                          QObject *receiver, const char *signal, int minMIdx,
  1199                                          bool buildSignature)
  1208                                          bool buildSignature)
  1200 {
  1209 {
  1201     QByteArray normalizedName = signal + 1;
  1210     QByteArray normalizedName = signal + 1;
  1202     hook.midx = findSlot(receiver, signal + 1, hook.params);
  1211     hook.midx = findSlot(receiver, signal + 1, hook.params);
  1212 
  1221 
  1213     hook.service = service;
  1222     hook.service = service;
  1214     hook.owner = owner; // we don't care if the service has an owner yet
  1223     hook.owner = owner; // we don't care if the service has an owner yet
  1215     hook.path = path;
  1224     hook.path = path;
  1216     hook.obj = receiver;
  1225     hook.obj = receiver;
       
  1226     hook.argumentMatch = argMatch;
  1217 
  1227 
  1218     // build the D-Bus signal name and signature
  1228     // build the D-Bus signal name and signature
  1219     // This should not happen for QDBusConnection::connect, use buildSignature here, since
  1229     // This should not happen for QDBusConnection::connect, use buildSignature here, since
  1220     // QDBusConnection::connect passes false and everything else uses true
  1230     // QDBusConnection::connect passes false and everything else uses true
  1221     QString mname = name;
  1231     QString mname = name;
  1233         for (int i = 1; i < hook.params.count(); ++i)
  1243         for (int i = 1; i < hook.params.count(); ++i)
  1234             if (hook.params.at(i) != QDBusMetaTypeId::message)
  1244             if (hook.params.at(i) != QDBusMetaTypeId::message)
  1235                 hook.signature += QLatin1String( QDBusMetaType::typeToSignature( hook.params.at(i) ) );
  1245                 hook.signature += QLatin1String( QDBusMetaType::typeToSignature( hook.params.at(i) ) );
  1236     }
  1246     }
  1237 
  1247 
  1238     hook.matchRule = buildMatchRule(service, owner, path, interface, mname, hook.signature);
  1248     hook.matchRule = buildMatchRule(service, owner, path, interface, mname, argMatch, hook.signature);
  1239     return true;                // connect to this signal
  1249     return true;                // connect to this signal
  1240 }
  1250 }
  1241 
  1251 
  1242 void QDBusConnectionPrivate::sendError(const QDBusMessage &msg, QDBusError::ErrorType code)
  1252 void QDBusConnectionPrivate::sendError(const QDBusMessage &msg, QDBusError::ErrorType code)
  1243 {
  1253 {
  1476     SignalHookHash::const_iterator end = signalHooks.constEnd();
  1486     SignalHookHash::const_iterator end = signalHooks.constEnd();
  1477     //qDebug("looking for: %s", path.toLocal8Bit().constData());
  1487     //qDebug("looking for: %s", path.toLocal8Bit().constData());
  1478     //qDBusDebug() << signalHooks.keys();
  1488     //qDBusDebug() << signalHooks.keys();
  1479     for ( ; it != end && it.key() == key; ++it) {
  1489     for ( ; it != end && it.key() == key; ++it) {
  1480         const SignalHook &hook = it.value();
  1490         const SignalHook &hook = it.value();
  1481         if (!hook.owner.isEmpty() && hook.owner != msg.service())
  1491         if (!hook.owner.isNull() && hook.owner != msg.service())
  1482             continue;
  1492             continue;
  1483         if (!hook.path.isEmpty() && hook.path != msg.path())
  1493         if (!hook.path.isEmpty() && hook.path != msg.path())
  1484             continue;
  1494             continue;
  1485         if (!hook.signature.isEmpty() && hook.signature != msg.signature())
  1495         if (!hook.signature.isEmpty() && hook.signature != msg.signature())
  1486             continue;
  1496             continue;
  1487         if (hook.signature.isEmpty() && !hook.signature.isNull() && !msg.signature().isEmpty())
  1497         if (hook.signature.isEmpty() && !hook.signature.isNull() && !msg.signature().isEmpty())
  1488             continue;
  1498             continue;
       
  1499         if (!hook.argumentMatch.isEmpty()) {
       
  1500             const QVariantList arguments = msg.arguments();
       
  1501             if (hook.argumentMatch.size() > arguments.size())
       
  1502                 continue;
       
  1503 
       
  1504             bool matched = true;
       
  1505             for (int i = 0; i < hook.argumentMatch.size(); ++i) {
       
  1506                 const QString &param = hook.argumentMatch.at(i);
       
  1507                 if (param.isNull())
       
  1508                     continue;   // don't try to match against this
       
  1509                 if (param == arguments.at(i).toString())
       
  1510                     continue;   // matched
       
  1511                 matched = false;
       
  1512                 break;
       
  1513             }
       
  1514             if (!matched)
       
  1515                 continue;
       
  1516         }
  1489 
  1517 
  1490         activateSignal(hook, msg);
  1518         activateSignal(hook, msg);
  1491     }
  1519     }
  1492 }
  1520 }
  1493 
  1521 
  1618             return;
  1646             return;
  1619         }
  1647         }
  1620 
  1648 
  1621         baseService = QString::fromUtf8(service);
  1649         baseService = QString::fromUtf8(service);
  1622     } else {
  1650     } else {
  1623         qWarning("QDBusConnectionPrivate::SetConnection: Unable to get base service");
  1651         qWarning("QDBusConnectionPrivate::setConnection: Unable to get base service");
  1624     }
  1652     }
       
  1653 
       
  1654     QString busService = QLatin1String(DBUS_SERVICE_DBUS);
       
  1655     connectSignal(busService, QString(), QString(), QString(), QLatin1String("NameAcquired"), QStringList(), QString(),
       
  1656                   this, SLOT(registerService(QString)));
       
  1657     connectSignal(busService, QString(), QString(), QString(), QLatin1String("NameLost"), QStringList(), QString(),
       
  1658                   this, SLOT(unregisterService(QString)));
       
  1659 
  1625 
  1660 
  1626     q_dbus_connection_add_filter(connection, qDBusSignalFilter, this, 0);
  1661     q_dbus_connection_add_filter(connection, qDBusSignalFilter, this, 0);
  1627 
  1662 
  1628     //qDebug("base service: %s", service);
  1663     //qDebug("base service: %s", service);
  1629 
  1664 
  1931     }
  1966     }
  1932 
  1967 
  1933     return 1;
  1968     return 1;
  1934 }
  1969 }
  1935 
  1970 
       
  1971 bool QDBusConnectionPrivate::connectSignal(const QString &service, const QString &owner,
       
  1972                                            const QString &path, const QString &interface, const QString &name,
       
  1973                                            const QStringList &argumentMatch, const QString &signature,
       
  1974                                            QObject *receiver, const char *slot)
       
  1975 {
       
  1976     // check the slot
       
  1977     QDBusConnectionPrivate::SignalHook hook;
       
  1978     QString key;
       
  1979     QString name2 = name;
       
  1980     if (name2.isNull())
       
  1981         name2.detach();
       
  1982 
       
  1983     hook.signature = signature;
       
  1984     if (!prepareHook(hook, key, service, owner, path, interface, name, argumentMatch, receiver, slot, 0, false))
       
  1985         return false;           // don't connect
       
  1986 
       
  1987     // avoid duplicating:
       
  1988     QDBusConnectionPrivate::SignalHookHash::ConstIterator it = signalHooks.find(key);
       
  1989     QDBusConnectionPrivate::SignalHookHash::ConstIterator end = signalHooks.constEnd();
       
  1990     for ( ; it != end && it.key() == key; ++it) {
       
  1991         const QDBusConnectionPrivate::SignalHook &entry = it.value();
       
  1992         if (entry.service == hook.service &&
       
  1993             entry.owner == hook.owner &&
       
  1994             entry.path == hook.path &&
       
  1995             entry.signature == hook.signature &&
       
  1996             entry.obj == hook.obj &&
       
  1997             entry.midx == hook.midx) {
       
  1998             // no need to compare the parameters if it's the same slot
       
  1999             return true;        // already there
       
  2000         }
       
  2001     }
       
  2002 
       
  2003     connectSignal(key, hook);
       
  2004     return true;
       
  2005 }
       
  2006 
  1936 void QDBusConnectionPrivate::connectSignal(const QString &key, const SignalHook &hook)
  2007 void QDBusConnectionPrivate::connectSignal(const QString &key, const SignalHook &hook)
  1937 {
  2008 {
  1938     signalHooks.insertMulti(key, hook);
  2009     signalHooks.insertMulti(key, hook);
  1939     connect(hook.obj, SIGNAL(destroyed(QObject*)), SLOT(objectDestroyed(QObject*)),
  2010     connect(hook.obj, SIGNAL(destroyed(QObject*)), SLOT(objectDestroyed(QObject*)),
  1940             Qt::DirectConnection);
  2011             Qt::ConnectionType(Qt::DirectConnection | Qt::UniqueConnection));
  1941 
  2012 
  1942     MatchRefCountHash::iterator it = matchRefCounts.find(hook.matchRule);
  2013     MatchRefCountHash::iterator it = matchRefCounts.find(hook.matchRule);
  1943 
  2014 
  1944     if (it != matchRefCounts.end()) { // Match already present
  2015     if (it != matchRefCounts.end()) { // Match already present
  1945         it.value() = it.value() + 1;
  2016         it.value() = it.value() + 1;
  1958                      "while connecting signal to %s::%s: %s (%s)",
  2029                      "while connecting signal to %s::%s: %s (%s)",
  1959                      hook.obj->metaObject()->className(),
  2030                      hook.obj->metaObject()->className(),
  1960                      hook.obj->metaObject()->method(hook.midx).signature(),
  2031                      hook.obj->metaObject()->method(hook.midx).signature(),
  1961                      qPrintable(qerror.name()), qPrintable(qerror.message()));
  2032                      qPrintable(qerror.name()), qPrintable(qerror.message()));
  1962             Q_ASSERT(false);
  2033             Q_ASSERT(false);
  1963         }
  2034         } else {
  1964     }
  2035             // Successfully connected the signal
       
  2036             // Do we need to watch for this name?
       
  2037             if (shouldWatchService(hook.service)) {
       
  2038                 WatchedServicesHash::Iterator it = watchedServiceNames.find(hook.service);
       
  2039                 if (it != watchedServiceNames.end()) {
       
  2040                     // already watching
       
  2041                     ++it.value();
       
  2042                 } else {
       
  2043                     // we need to watch for this service changing
       
  2044                     QString dbusServerService = QLatin1String(DBUS_SERVICE_DBUS);
       
  2045                     connectSignal(dbusServerService, dbusServerService, QString(), QLatin1String(DBUS_INTERFACE_DBUS),
       
  2046                                   QLatin1String("NameOwnerChanged"), QStringList() << hook.service, QString(),
       
  2047                                   this, SLOT(_q_serviceOwnerChanged(QString,QString,QString)));
       
  2048                 }
       
  2049             }
       
  2050         }
       
  2051     }
       
  2052 }
       
  2053 
       
  2054 bool QDBusConnectionPrivate::disconnectSignal(const QString &service,
       
  2055                                               const QString &path, const QString &interface, const QString &name,
       
  2056                                               const QStringList &argumentMatch, const QString &signature,
       
  2057                                               QObject *receiver, const char *slot)
       
  2058 {
       
  2059     // check the slot
       
  2060     QDBusConnectionPrivate::SignalHook hook;
       
  2061     QString key;
       
  2062     QString name2 = name;
       
  2063     if (name2.isNull())
       
  2064         name2.detach();
       
  2065 
       
  2066     hook.signature = signature;
       
  2067     if (!prepareHook(hook, key, service, QString(), path, interface, name, argumentMatch, receiver, slot, 0, false))
       
  2068         return false;           // don't disconnect
       
  2069 
       
  2070     // avoid duplicating:
       
  2071     QDBusConnectionPrivate::SignalHookHash::Iterator it = signalHooks.find(key);
       
  2072     QDBusConnectionPrivate::SignalHookHash::Iterator end = signalHooks.end();
       
  2073     for ( ; it != end && it.key() == key; ++it) {
       
  2074         const QDBusConnectionPrivate::SignalHook &entry = it.value();
       
  2075         if (entry.service == hook.service &&
       
  2076             //entry.owner == hook.owner &&
       
  2077             entry.path == hook.path &&
       
  2078             entry.signature == hook.signature &&
       
  2079             entry.obj == hook.obj &&
       
  2080             entry.midx == hook.midx) {
       
  2081             // no need to compare the parameters if it's the same slot
       
  2082             disconnectSignal(it);
       
  2083             return true;        // it was there
       
  2084         }
       
  2085     }
       
  2086 
       
  2087     // the slot was not found
       
  2088     return false;
  1965 }
  2089 }
  1966 
  2090 
  1967 QDBusConnectionPrivate::SignalHookHash::Iterator
  2091 QDBusConnectionPrivate::SignalHookHash::Iterator
  1968 QDBusConnectionPrivate::disconnectSignal(SignalHookHash::Iterator &it)
  2092 QDBusConnectionPrivate::disconnectSignal(SignalHookHash::Iterator &it)
  1969 {
  2093 {
  1970     const SignalHook &hook = it.value();
  2094     const SignalHook &hook = it.value();
       
  2095 
       
  2096     WatchedServicesHash::Iterator sit = watchedServiceNames.find(hook.service);
       
  2097     if (sit != watchedServiceNames.end()) {
       
  2098         if (sit.value() == 1) {
       
  2099             watchedServiceNames.erase(sit);
       
  2100             QString dbusServerService = QLatin1String(DBUS_SERVICE_DBUS);
       
  2101             disconnectSignal(dbusServerService, QString(), QLatin1String(DBUS_INTERFACE_DBUS),
       
  2102                           QLatin1String("NameOwnerChanged"), QStringList() << hook.service, QString(),
       
  2103                           this, SLOT(_q_serviceOwnerChanged(QString,QString,QString)));
       
  2104         } else {
       
  2105             --sit.value();
       
  2106         }
       
  2107     }
  1971 
  2108 
  1972     bool erase = false;
  2109     bool erase = false;
  1973     MatchRefCountHash::iterator i = matchRefCounts.find(hook.matchRule);
  2110     MatchRefCountHash::iterator i = matchRefCounts.find(hook.matchRule);
  1974     if (i == matchRefCounts.end()) {
  2111     if (i == matchRefCounts.end()) {
  1975         qWarning("QDBusConnectionPrivate::disconnectSignal: MatchRule not found in matchRefCounts!!");
  2112         qWarning("QDBusConnectionPrivate::disconnectSignal: MatchRule not found in matchRefCounts!!");
  2025     // this function is called by QDBusAbstractInterface when one of its signals is connected
  2162     // this function is called by QDBusAbstractInterface when one of its signals is connected
  2026     // we set up a relay from D-Bus into it
  2163     // we set up a relay from D-Bus into it
  2027     SignalHook hook;
  2164     SignalHook hook;
  2028     QString key;
  2165     QString key;
  2029 
  2166 
  2030     if (!prepareHook(hook, key, service, owner, path, interface, QString(), receiver, signal,
  2167     if (!prepareHook(hook, key, service, owner, path, interface, QString(), QStringList(), receiver, signal,
  2031                      QDBusAbstractInterface::staticMetaObject.methodCount(), true))
  2168                      QDBusAbstractInterface::staticMetaObject.methodCount(), true))
  2032         return;                 // don't connect
  2169         return;                 // don't connect
  2033 
  2170 
  2034     // add it to our list:
  2171     // add it to our list:
  2035     QDBusWriteLocker locker(ConnectRelayAction, this);
  2172     QDBusWriteLocker locker(ConnectRelayAction, this);
  2057     // this function is called by QDBusAbstractInterface when one of its signals is disconnected
  2194     // this function is called by QDBusAbstractInterface when one of its signals is disconnected
  2058     // we remove relay from D-Bus into it
  2195     // we remove relay from D-Bus into it
  2059     SignalHook hook;
  2196     SignalHook hook;
  2060     QString key;
  2197     QString key;
  2061 
  2198 
  2062     if (!prepareHook(hook, key, service, owner, path, interface, QString(), receiver, signal,
  2199     if (!prepareHook(hook, key, service, owner, path, interface, QString(), QStringList(), receiver, signal,
  2063                      QDBusAbstractInterface::staticMetaObject.methodCount(), true))
  2200                      QDBusAbstractInterface::staticMetaObject.methodCount(), true))
  2064         return;                 // don't connect
  2201         return;                 // don't connect
  2065 
  2202 
  2066     // remove it from our list:
  2203     // remove it from our list:
  2067     QDBusWriteLocker locker(DisconnectRelayAction, this);
  2204     QDBusWriteLocker locker(DisconnectRelayAction, this);