summaryrefslogtreecommitdiff
path: root/src/gdbusxx
diff options
context:
space:
mode:
authorPatrick Ohly <patrick.ohly@intel.com>2012-08-27 16:44:34 +0200
committerPatrick Ohly <patrick.ohly@intel.com>2012-09-26 08:36:35 +0200
commitdb3f5d69db90890c0b0c120ab41a207b750a8b65 (patch)
tree9c781e04f03f1e140625fcc24cc9a79a951d3fef /src/gdbusxx
parent36204047b367fe995947fbdfe700273d6580e032 (diff)
GDBus GIO: more flexible SignalWatch
Working with obexd 0.47's Transfer object depends on path prefix filtering of signals to be efficient. This patch adds that support in the GDBus C++ bindings, together with some documentation on how to use it. It also allows relaxing the signal matching by interpreting empty strings as "match any". Finally it adds support for passing of sender, path, interface and member (= signal name) information to the user of the D-Bus binding. This is necessary because a handler with wildcards for matching cannot know the actual values unless they are passed to the callback explicitly. This was not possible at all before for signals (because extracting that information relied on a message pointer, which wasn't set while decoding signal messages) and limited to just the sender in method implementations. Besides in functionality, GDBus for GIO and for libdbus now also differ slightly in their API. In order to pass meta data about a signal into the demarshalling get() methods, they have to be stored in ExtractArgs first and passed to get() via that struct, which is an interface change. Derived code which extends marshalling/demarshalling needs to be adapted accordingly. Allowing some ifdefs elsewhere seems simpler than adapting GDBus for libdbus. Those ifdefs can be removed once libdbus support is no longer needed. The patch relaxes access control to DBusObject and DBusRemoteObject because that simplifies the code which works better with std::string. Traditionally the sender of a signal was not checked. This is probably related to unsolved questions like "should watching org.foo.bar follow owner changes", but not doing any checking is just not right either. Not fixed in this patch.
Diffstat (limited to 'src/gdbusxx')
-rw-r--r--src/gdbusxx/gdbus-cxx-bridge.cpp8
-rw-r--r--src/gdbusxx/gdbus-cxx-bridge.h494
-rw-r--r--src/gdbusxx/gdbus-cxx.h36
3 files changed, 451 insertions, 87 deletions
diff --git a/src/gdbusxx/gdbus-cxx-bridge.cpp b/src/gdbusxx/gdbus-cxx-bridge.cpp
index 7056a2ed..ea6aaefe 100644
--- a/src/gdbusxx/gdbus-cxx-bridge.cpp
+++ b/src/gdbusxx/gdbus-cxx-bridge.cpp
@@ -424,11 +424,11 @@ Watch::~Watch()
}
}
-void getWatch(GDBusConnection *conn, GDBusMessage *msg,
- GVariantIter &iter, boost::shared_ptr<Watch> &value)
+void getWatch(ExtractArgs &context,
+ boost::shared_ptr<Watch> &value)
{
- std::auto_ptr<Watch> watch(new Watch(conn));
- watch->activate(g_dbus_message_get_sender(msg));
+ std::auto_ptr<Watch> watch(new Watch(context.m_conn));
+ watch->activate(g_dbus_message_get_sender(context.m_msg));
value.reset(watch.release());
}
diff --git a/src/gdbusxx/gdbus-cxx-bridge.h b/src/gdbusxx/gdbus-cxx-bridge.h
index de67a1de..ceab5e4b 100644
--- a/src/gdbusxx/gdbus-cxx-bridge.h
+++ b/src/gdbusxx/gdbus-cxx-bridge.h
@@ -61,6 +61,7 @@
#include <gio/gio.h>
#include <map>
+#include <list>
#include <vector>
#include <utility>
@@ -70,6 +71,7 @@
#include <boost/variant.hpp>
#include <boost/variant/get.hpp>
#include <boost/algorithm/string/predicate.hpp>
+#include <boost/algorithm/string/join.hpp>
#include <boost/tuple/tuple.hpp>
/* The SyncEvolution exception handler must integrate into the D-Bus
@@ -81,6 +83,10 @@
* remove the define once the in-tree gdbus is dropped. */
#define DBUS_NEW_ERROR_MSG g_dbus_message_new_method_error
+// allow some code to deal with API differences betwee GDBus C++ for GIO
+// and the version for libdbus
+#define GDBUS_CXX_GIO 1
+
// Boost docs want this in the boost:: namespace, but
// that fails with clang 2.9 depending on the inclusion order of
// header files. Global namespace works in all cases.
@@ -412,11 +418,21 @@ template<class A> struct Set <A &>
* ultimately on the prototype of the method.
*/
struct ExtractArgs {
+ // always set
GDBusConnection *m_conn;
+
+ // only set when handling a method call
GDBusMessage *m_msg;
GVariantIter m_iter;
+ // only set when m_msg is NULL (happens when handling signal)
+ const char *m_sender;
+ const char *m_path;
+ const char *m_interface;
+ const char *m_signal;
+
public:
+ /** constructor for parsing a method invocation message, which must not be NULL */
ExtractArgs(GDBusConnection *conn, GDBusMessage *msg) {
m_conn = conn;
m_msg = msg;
@@ -427,6 +443,21 @@ struct ExtractArgs {
}
}
+ /** constructor for parsing signal parameters */
+ ExtractArgs(GDBusConnection *conn,
+ const char *sender,
+ const char *path,
+ const char *interface,
+ const char *signal)
+ {
+ m_conn = conn;
+ m_msg = NULL;
+ m_sender = sender;
+ m_path = path;
+ m_interface = interface;
+ m_signal = signal;
+ }
+
/** syntactic sugar: redirect >> into Get instance */
template<class A> ExtractArgs & operator >> (const A &a) {
return a.get(*this);
@@ -439,7 +470,7 @@ template<class A> struct Get
A &m_a;
Get(A &a) : m_a(a) {}
ExtractArgs &get(ExtractArgs &context) const {
- dbus_traits<A>::get(context.m_conn, context.m_msg, context.m_iter, m_a);
+ dbus_traits<A>::get(context, context.m_iter, m_a);
return context;
}
};
@@ -450,7 +481,7 @@ template<class A> struct Get <const A &>
A &m_a;
Get(A &a) : m_a(a) {}
ExtractArgs &get(ExtractArgs &context) const {
- dbus_traits<A>::get(context.m_conn, context.m_msg, context.m_iter, m_a);
+ dbus_traits<A>::get(context, context.m_iter, m_a);
return context;
}
};
@@ -469,9 +500,12 @@ template<class A> struct Get <A &>
*/
class DBusObject
{
+ protected:
DBusConnectionPtr m_conn;
std::string m_path;
std::string m_interface;
+
+ private:
bool m_closeConnection;
public:
@@ -513,6 +547,7 @@ class DBusObject
*/
class DBusRemoteObject : public DBusObject
{
+ protected:
std::string m_destination;
public:
DBusRemoteObject(const DBusConnectionPtr &conn,
@@ -1239,7 +1274,7 @@ template<class host, class VariantTraits> struct basic_marshal : public dbus_tra
/**
* copy value from GVariant iterator into variable
*/
- static void get(GDBusConnection *conn, GDBusMessage *msg,
+ static void get(ExtractArgs &context,
GVariantIter &iter, host &value)
{
GVariant *var = g_variant_iter_next_value(&iter);
@@ -1288,10 +1323,10 @@ template<> struct dbus_traits<int8_t> : dbus_traits<uint8_t>
typedef int8_t host_type;
typedef int8_t arg_type;
- static void get(GDBusConnection *conn, GDBusMessage *msg,
+ static void get(ExtractArgs &context,
GVariantIter &iter, host_type &value)
{
- dbus_traits<uint8_t>::get(conn, msg, iter, reinterpret_cast<uint8_t &>(value));
+ dbus_traits<uint8_t>::get(context, iter, reinterpret_cast<uint8_t &>(value));
}
};
@@ -1336,7 +1371,7 @@ template<> struct dbus_traits<bool> : public dbus_traits_base
typedef bool host_type;
typedef bool arg_type;
- static void get(GDBusConnection *conn, GDBusMessage *msg,
+ static void get(ExtractArgs &context,
GVariantIter &iter, bool &value)
{
GVariant *var = g_variant_iter_next_value(&iter);
@@ -1363,7 +1398,7 @@ template<> struct dbus_traits<std::string> : public dbus_traits_base
static std::string getSignature() {return getType(); }
static std::string getReply() { return ""; }
- static void get(GDBusConnection *conn, GDBusMessage *msg,
+ static void get(ExtractArgs &context,
GVariantIter &iter, std::string &value)
{
GVariant *var = g_variant_iter_next_value(&iter);
@@ -1391,7 +1426,7 @@ template <> struct dbus_traits<DBusObject_t> : public dbus_traits_base
static std::string getSignature() {return getType(); }
static std::string getReply() { return ""; }
- static void get(GDBusConnection *conn, GDBusMessage *msg,
+ static void get(ExtractArgs &context,
GVariantIter &iter, DBusObject_t &value)
{
GVariant *var = g_variant_iter_next_value(&iter);
@@ -1425,10 +1460,12 @@ template <> struct dbus_traits<Caller_t> : public dbus_traits_base
static std::string getSignature() { return ""; }
static std::string getReply() { return ""; }
- static void get(GDBusConnection *conn, GDBusMessage *msg,
+ static void get(ExtractArgs &context,
GVariantIter &iter, Caller_t &value)
{
- const char *peer = g_dbus_message_get_sender(msg);
+ const char *peer = context.m_msg ?
+ g_dbus_message_get_sender(context.m_msg) :
+ context.m_sender;
if (!peer) {
throw std::runtime_error("D-Bus method call without sender?!");
}
@@ -1440,6 +1477,84 @@ template <> struct dbus_traits<Caller_t> : public dbus_traits_base
};
/**
+ * pseudo-parameter: not part of D-Bus signature,
+ * but rather extracted from message attributes
+ */
+template <> struct dbus_traits<Path_t> : public dbus_traits_base
+{
+ static std::string getType() { return ""; }
+ static std::string getSignature() { return ""; }
+ static std::string getReply() { return ""; }
+
+ static void get(ExtractArgs &context,
+ GVariantIter &iter, Path_t &value)
+ {
+ const char *path = context.m_msg ?
+ g_dbus_message_get_path(context.m_msg) :
+ context.m_path;
+ if (!path) {
+ throw std::runtime_error("D-Bus message without path?!");
+ }
+ value = path;
+ }
+
+ typedef Path_t host_type;
+ typedef const Path_t &arg_type;
+};
+
+/**
+ * pseudo-parameter: not part of D-Bus signature,
+ * but rather extracted from message attributes
+ */
+template <> struct dbus_traits<Interface_t> : public dbus_traits_base
+{
+ static std::string getType() { return ""; }
+ static std::string getSignature() { return ""; }
+ static std::string getReply() { return ""; }
+
+ static void get(ExtractArgs &context,
+ GVariantIter &iter, Interface_t &value)
+ {
+ const char *path = context.m_msg ?
+ g_dbus_message_get_path(context.m_msg) :
+ context.m_path;
+ if (!path) {
+ throw std::runtime_error("D-Bus message without path?!");
+ }
+ value = path;
+ }
+
+ typedef Interface_t host_type;
+ typedef const Interface_t &arg_type;
+};
+
+/**
+ * pseudo-parameter: not part of D-Bus signature,
+ * but rather extracted from message attributes
+ */
+template <> struct dbus_traits<Member_t> : public dbus_traits_base
+{
+ static std::string getType() { return ""; }
+ static std::string getSignature() { return ""; }
+ static std::string getReply() { return ""; }
+
+ static void get(ExtractArgs &context,
+ GVariantIter &iter, Member_t &value)
+ {
+ const char *path = context.m_msg ?
+ g_dbus_message_get_path(context.m_msg) :
+ context.m_path;
+ if (!path) {
+ throw std::runtime_error("D-Bus message without path?!");
+ }
+ value = path;
+ }
+
+ typedef Member_t host_type;
+ typedef const Member_t &arg_type;
+};
+
+/**
* a std::pair - maps to D-Bus struct
*/
template<class A, class B> struct dbus_traits< std::pair<A,B> > : public dbus_traits_base
@@ -1457,7 +1572,7 @@ template<class A, class B> struct dbus_traits< std::pair<A,B> > : public dbus_tr
typedef std::pair<A,B> host_type;
typedef const std::pair<A,B> &arg_type;
- static void get(GDBusConnection *conn, GDBusMessage *msg,
+ static void get(ExtractArgs &context,
GVariantIter &iter, host_type &pair)
{
GVariant *var = g_variant_iter_next_value(&iter);
@@ -1467,8 +1582,8 @@ template<class A, class B> struct dbus_traits< std::pair<A,B> > : public dbus_tr
GVariantIter tupIter;
g_variant_iter_init(&tupIter, var);
- dbus_traits<A>::get(conn, msg, tupIter, pair.first);
- dbus_traits<B>::get(conn, msg, tupIter, pair.second);
+ dbus_traits<A>::get(context, tupIter, pair.first);
+ dbus_traits<B>::get(context, tupIter, pair.second);
g_variant_unref(var);
}
@@ -1521,7 +1636,7 @@ template<class V> struct dbus_traits< DBusArray<V> > : public dbus_traits_base
typedef DBusArray<V> host_type;
typedef const host_type &arg_type;
- static void get(GDBusConnection *conn, GDBusMessage *msg,
+ static void get(ExtractArgs &context,
GVariantIter &iter, host_type &array)
{
GVariant *var = g_variant_iter_next_value(&iter);
@@ -1573,7 +1688,7 @@ template<class K, class V, class C> struct dbus_traits< std::map<K, V, C> > : pu
typedef std::map<K, V, C> host_type;
typedef const host_type &arg_type;
- static void get(GDBusConnection *conn, GDBusMessage *msg,
+ static void get(ExtractArgs &context,
GVariantIter &iter, host_type &dict)
{
GVariant *var = g_variant_iter_next_value(&iter);
@@ -1589,8 +1704,8 @@ template<class K, class V, class C> struct dbus_traits< std::map<K, V, C> > : pu
V value;
GVariantIter childIter;
g_variant_iter_init(&childIter, child);
- dbus_traits<K>::get(conn, msg, childIter, key);
- dbus_traits<V>::get(conn, msg, childIter, value);
+ dbus_traits<K>::get(context, childIter, key);
+ dbus_traits<V>::get(context, childIter, value);
dict.insert(std::make_pair(key, value));
g_variant_unref(child);
}
@@ -1634,7 +1749,7 @@ template<class V> struct dbus_traits< std::vector<V> > : public dbus_traits_base
typedef std::vector<V> host_type;
typedef const std::vector<V> &arg_type;
- static void get(GDBusConnection *conn, GDBusMessage *msg,
+ static void get(ExtractArgs &context,
GVariantIter &iter, host_type &array)
{
GVariant *var = g_variant_iter_next_value(&iter);
@@ -1647,7 +1762,7 @@ template<class V> struct dbus_traits< std::vector<V> > : public dbus_traits_base
g_variant_iter_init(&childIter, var);
for(int i = 0; i < nelements; ++i) {
V value;
- dbus_traits<V>::get(conn, msg, childIter, value);
+ dbus_traits<V>::get(context, childIter, value);
array.push_back(value);
}
g_variant_unref(var);
@@ -1698,7 +1813,7 @@ template <class V> struct dbus_traits <boost::variant <V> > : public dbus_traits
static std::string getSignature() { return getType(); }
static std::string getReply() { return ""; }
- static void get(GDBusConnection *conn, GDBusMessage *msg,
+ static void get(ExtractArgs &context,
GVariantIter &iter, boost::variant <V> &value)
{
GVariant *var = g_variant_iter_next_value(&iter);
@@ -1718,7 +1833,7 @@ template <class V> struct dbus_traits <boost::variant <V> > : public dbus_traits
V val;
// Note: Reset the iterator so that the call to dbus_traits<V>::get() will get the right variant;
g_variant_iter_init(&varIter, var);
- dbus_traits<V>::get(conn, msg, varIter, val);
+ dbus_traits<V>::get(context, varIter, val);
value = val;
g_variant_unref(var);
@@ -1748,7 +1863,7 @@ template <class V1, class V2> struct dbus_traits <boost::variant <V1, V2> > : pu
static std::string getSignature() { return getType(); }
static std::string getReply() { return ""; }
- static void get(GDBusConnection *conn, GDBusMessage *msg,
+ static void get(ExtractArgs &context,
GVariantIter &iter, boost::variant <V1, V2> &value)
{
GVariant *var = g_variant_iter_next_value(&iter);
@@ -1769,13 +1884,13 @@ template <class V1, class V2> struct dbus_traits <boost::variant <V1, V2> > : pu
V1 val;
// Note: Reset the iterator so that the call to dbus_traits<V>::get() will get the right variant;
g_variant_iter_init(&varIter, var);
- dbus_traits<V1>::get(conn, msg, varIter, val);
+ dbus_traits<V1>::get(context, varIter, val);
value = val;
} else {
V2 val;
// Note: Reset the iterator so that the call to dbus_traits<V>::get() will get the right variant;
g_variant_iter_init(&varIter, var);
- dbus_traits<V2>::get(conn, msg, varIter, val);
+ dbus_traits<V2>::get(context, varIter, val);
value = val;
}
g_variant_unref(var);
@@ -1804,10 +1919,10 @@ template<class K, class V, V K::*m> struct dbus_member_single
}
typedef V host_type;
- static void get(GDBusConnection *conn, GDBusMessage *msg,
+ static void get(ExtractArgs &context,
GVariantIter &iter, K &val)
{
- dbus_traits<V>::get(conn, msg, iter, val.*m);
+ dbus_traits<V>::get(context, iter, val.*m);
}
static void append(GVariantBuilder &builder, const K &val)
@@ -1828,11 +1943,11 @@ template<class K, class V, V K::*m, class M> struct dbus_member
}
typedef V host_type;
- static void get(GDBusConnection *conn, GDBusMessage *msg,
+ static void get(ExtractArgs &context,
GVariantIter &iter, K &val)
{
- dbus_traits<V>::get(conn, msg, iter, val.*m);
- M::get(conn, msg, iter, val);
+ dbus_traits<V>::get(context, iter, val.*m);
+ M::get(context, iter, val);
}
static void append(GVariantBuilder &builder, const K &val)
@@ -1867,7 +1982,7 @@ template<class K, class M> struct dbus_struct_traits : public dbus_traits_base
typedef K host_type;
typedef const K &arg_type;
- static void get(GDBusConnection *conn, GDBusMessage *msg,
+ static void get(ExtractArgs &context,
GVariantIter &iter, host_type &val)
{
GVariant *var = g_variant_iter_next_value(&iter);
@@ -1877,7 +1992,7 @@ template<class K, class M> struct dbus_struct_traits : public dbus_traits_base
GVariantIter tupIter;
g_variant_iter_init(&tupIter, var);
- M::get(conn, msg, tupIter, val);
+ M::get(context, tupIter, val);
g_variant_unref(var);
}
@@ -1902,11 +2017,11 @@ template<class E, class I> struct dbus_enum_traits : public dbus_traits<I>
// cast from enum to int in append() is implicit; in
// get() we have to make it explicit
- static void get(GDBusConnection *conn, GDBusMessage *msg,
+ static void get(ExtractArgs &context,
GVariantIter &iter, host_type &val)
{
I ival;
- dbus_traits<I>::get(conn, msg, iter, ival);
+ dbus_traits<I>::get(context, iter, ival);
val = static_cast<E>(ival);
}
};
@@ -2011,8 +2126,7 @@ class Watch : private boost::noncopyable
void activate(const char *peer);
};
-void getWatch(GDBusConnection *conn, GDBusMessage *msg,
- GVariantIter &iter, boost::shared_ptr<Watch> &value);
+void getWatch(ExtractArgs &context, boost::shared_ptr<Watch> &value);
/**
* pseudo-parameter: not part of D-Bus signature,
@@ -2024,8 +2138,8 @@ template <> struct dbus_traits< boost::shared_ptr<Watch> > : public dbus_traits
static std::string getSignature() { return ""; }
static std::string getReply() { return ""; }
- static void get(GDBusConnection *conn, GDBusMessage *msg,
- GVariantIter &iter, boost::shared_ptr<Watch> &value) { getWatch(conn, msg, iter, value); }
+ static void get(ExtractArgs &context,
+ GVariantIter &iter, boost::shared_ptr<Watch> &value) { getWatch(context, value); }
static void append(GVariantBuilder &builder, const boost::shared_ptr<Watch> &value) {}
typedef boost::shared_ptr<Watch> host_type;
@@ -2431,10 +2545,10 @@ template <class R, class DBusR> struct dbus_traits_result
typedef boost::shared_ptr<R> &arg_type;
static const bool asynchronous = true;
- static void get(GDBusConnection *conn, GDBusMessage *msg,
+ static void get(ExtractArgs &context,
GVariantIter &iter, host_type &value)
{
- value.reset(new DBusR(conn, msg));
+ value.reset(new DBusR(context.m_conn, context.m_msg));
}
};
@@ -4513,26 +4627,161 @@ public:
};
/**
+ * Describes which signals are meant to be received by the callback in
+ * a SignalWatch. Only available when using GDBus C++ for GIO (this
+ * code here). GDBus libdbus only supports passing a DBusRemoteObject
+ * to the SignalWatch constructors, which does a match based on object
+ * path, interface name, and signal name.
+ *
+ * Traditionally, all three strings had to be given. Now if any of
+ * them is empty, it is excluded from the filter entirely (any string
+ * matches).
+ *
+ * Using this class adds the possibility to do a path prefix match or,
+ * in the future, other kind of matches.
+ *
+ * TODO: add support for filtering by sender. Not doing so leads to
+ * a situation where a malicious local process can fake signals.
+ *
+ * Avoid situations where different SignalWatches use exactly the same
+ * filter. Creating both watches will work, but when one is destroyed
+ * it might happen that the process stops receiving signals from the
+ * D-Bus daemon because the watch for that signal filter was removed
+ * from the connection (neither D-Bus spec nor D-Bus GIO guarantee
+ * that the match is ref counted).
+ */
+class SignalFilter : public DBusRemoteObject
+{
+ public:
+ enum Flags {
+ SIGNAL_FILTER_NONE,
+ /**
+ * Normally, a path must match completely. With this flag set,
+ * any signal which has the given path as prefix
+ * matches.
+ *
+ * The path filter must not end in a slash.
+ *
+ * Example: "/foo/bar/xyz" matches "/foo/bar" only if
+ * SIGNAL_FILTER_PATH_PREFIX is set. "/foo/barxyz" does not
+ * match it in any case.
+ *
+ */
+ SIGNAL_FILTER_PATH_PREFIX = 1<<0
+ };
+
+ /**
+ * Match based on object path and interface as stored in object
+ * and based on signal name as passed separately. Does a full
+ * match of all unless a a string is empty, in which case
+ * that criteria is ignored.
+ */
+ SignalFilter(const DBusRemoteObject &object,
+ const std::string &signal) :
+ DBusRemoteObject(object),
+ m_signal(signal),
+ m_flags(SIGNAL_FILTER_NONE)
+ {}
+
+ /**
+ * Full control over filtering.
+ */
+ SignalFilter(const DBusConnectionPtr &conn,
+ const std::string &path,
+ const std::string &interface,
+ const std::string &signal,
+ Flags flags) :
+ DBusRemoteObject(conn, path, interface, ""),
+ m_flags(flags)
+ {}
+
+ const char *getSignal() const { return m_signal.c_str(); }
+ Flags getFlags() const { return Flags(m_flags); }
+
+ /**
+ * Check that current signal matches the filter. Necessary
+ * because GIO D-Bus does not know about "path_namespace" and
+ * because G_DBUS_SIGNAL_FLAGS_NO_MATCH_RULE seems to disable all
+ * signal filtering by GIO.
+ */
+ bool matches(const ExtractArgs &context) const
+ {
+ return
+ (m_path.empty() || m_path == context.m_path) &&
+ (m_interface.empty() || m_interface == context.m_interface) &&
+ (m_signal.empty() || m_signal == context.m_signal) &&
+ (m_path.empty() ||
+ ((m_flags & SIGNAL_FILTER_PATH_PREFIX) ?
+ (strlen(context.m_path) > m_path.size() &&
+ !m_path.compare(0, m_path.size(),
+ context.m_path, m_path.size()) &&
+ context.m_path[m_path.size()] == '/') :
+ m_path == context.m_path));
+ }
+
+ private:
+ std::string m_signal;
+ Flags m_flags;
+};
+
+/**
+ * Helper class for builting a match rule.
+ */
+class Criteria : public std::list<std::string> {
+ public:
+ void add(const char *keyword, const char *value) {
+ if (value && value[0]) {
+ std::string buffer;
+ buffer.reserve(strlen(keyword) + strlen(value) + 3);
+ buffer += keyword;
+ buffer += '=';
+ buffer += '\'';
+ buffer += value;
+ buffer += '\'';
+ push_back(buffer);
+ }
+ }
+
+ std::string createMatchRule() const { return boost::join(*this, ","); }
+};
+
+/**
* Common functionality of all SignalWatch* classes.
* @param T boost::function with the right signature
*/
-template <class T> class SignalWatch
+template <class T> class SignalWatch : public SignalFilter
{
public:
SignalWatch(const DBusRemoteObject &object,
const std::string &signal,
bool = true)
- : m_object(object), m_signal(signal), m_tag(0)
+ : SignalFilter(object, signal), m_tag(0), m_manualMatch(false)
+ {
+ }
+
+ SignalWatch(const SignalFilter &filter)
+ : SignalFilter(filter), m_tag(0), m_manualMatch(false)
{
}
~SignalWatch()
{
- if (m_tag) {
- GDBusConnection *connection = m_object.getConnection();
- if (connection) {
- g_dbus_connection_signal_unsubscribe(connection, m_tag);
+ try {
+ if (m_tag) {
+ GDBusConnection *connection = getConnection();
+ if (connection) {
+ g_dbus_connection_signal_unsubscribe(connection, m_tag);
+ }
+ }
+ if (m_manualMatch) {
+ DBusClientCall0(GDBusCXX::DBusRemoteObject(getConnection(),
+ "/org/freedesktop/DBus",
+ "org.freedesktop.DBus",
+ "org.freedesktop.DBus"),
+ "RemoveMatch")(m_matchRule);
}
+ } catch (...) {
+ // TODO (?): log error
}
}
@@ -4540,20 +4789,22 @@ template <class T> class SignalWatch
const Callback_t &getCallback() const { return m_callback; }
protected:
- const DBusRemoteObject &m_object;
- std::string m_signal;
guint m_tag;
T m_callback;
+ bool m_manualMatch;
+ std::string m_matchRule;
void activateInternal(const Callback_t &callback, GDBusSignalCallback cb)
{
m_callback = callback;
- m_tag = g_dbus_connection_signal_subscribe(m_object.getConnection(),
+ m_tag = g_dbus_connection_signal_subscribe(getConnection(),
NULL,
- m_object.getInterface(),
- m_signal.c_str(),
- m_object.getPath(),
+ getInterface()[0] ? getInterface() : NULL,
+ getSignal()[0] ? getSignal() : NULL,
+ (!(getFlags() & SIGNAL_FILTER_PATH_PREFIX) && getPath()[0]) ? getPath() : NULL,
NULL,
+ (getFlags() & SIGNAL_FILTER_PATH_PREFIX) ?
+ G_DBUS_SIGNAL_FLAGS_NO_MATCH_RULE :
G_DBUS_SIGNAL_FLAGS_NONE,
cb,
this,
@@ -4561,9 +4812,24 @@ template <class T> class SignalWatch
if (!m_tag) {
throw std::runtime_error(std::string("activating signal failed: ") +
- "path " + m_object.getPath() +
- " interface " + m_object.getInterface() +
- " member " + m_signal);
+ "path " + getPath() +
+ " interface " + getInterface() +
+ " member " + getSignal());
+ }
+ if (getFlags() & SignalFilter::SIGNAL_FILTER_PATH_PREFIX) {
+ // Need to set up match rules manually.
+ Criteria criteria;
+ criteria.push_back("type='signal'");
+ criteria.add("interface", getInterface());
+ criteria.add("member", getSignal());
+ criteria.add("path_namespace", getPath());
+ m_matchRule = criteria.createMatchRule();
+ DBusClientCall0(GDBusCXX::DBusRemoteObject(getConnection(),
+ "/org/freedesktop/DBus",
+ "org.freedesktop.DBus",
+ "org.freedesktop.DBus"),
+ "AddMatch")(m_matchRule);
+ m_manualMatch = true;
}
}
};
@@ -4580,6 +4846,10 @@ class SignalWatch0 : public SignalWatch< boost::function<void (void)> >
{
}
+ SignalWatch0(const SignalFilter &filter)
+ : SignalWatch<Callback_t>(filter)
+ {}
+
static void internalCallback(GDBusConnection *conn,
const gchar *sender,
const gchar *path,
@@ -4589,7 +4859,13 @@ class SignalWatch0 : public SignalWatch< boost::function<void (void)> >
gpointer data) throw ()
{
try {
- const Callback_t &cb = static_cast< SignalWatch<Callback_t> *>(data)->getCallback();
+ SignalWatch<Callback_t> *watch = static_cast< SignalWatch<Callback_t> *>(data);
+ ExtractArgs context(conn, sender, path, interface, signal);
+ if (!watch->matches(context)) {
+ return;
+ }
+
+ const Callback_t &cb = watch->getCallback();
cb();
} catch (const std::exception &ex) {
g_error("unexpected exception caught in internalCallback(): %s", ex.what());
@@ -4617,6 +4893,10 @@ class SignalWatch1 : public SignalWatch< boost::function<void (const A1 &)> >
{
}
+ SignalWatch1(const SignalFilter &filter)
+ : SignalWatch<Callback_t>(filter)
+ {}
+
static void internalCallback(GDBusConnection *conn,
const gchar *sender,
const gchar *path,
@@ -4626,13 +4906,18 @@ class SignalWatch1 : public SignalWatch< boost::function<void (const A1 &)> >
gpointer data) throw()
{
try {
- const Callback_t &cb = static_cast< SignalWatch<Callback_t> *>(data)->getCallback();
+ SignalWatch<Callback_t> *watch = static_cast< SignalWatch<Callback_t> *>(data);
+ ExtractArgs context(conn, sender, path, interface, signal);
+ if (!watch->matches(context)) {
+ return;
+ }
typename dbus_traits<A1>::host_type a1;
GVariantIter iter;
g_variant_iter_init(&iter, params);
- dbus_traits<A1>::get(conn, NULL, iter, a1);
+ dbus_traits<A1>::get(context, iter, a1);
+ const Callback_t &cb = watch->getCallback();
cb(a1);
} catch (const std::exception &ex) {
g_error("unexpected exception caught in internalCallback(): %s", ex.what());
@@ -4660,6 +4945,10 @@ class SignalWatch2 : public SignalWatch< boost::function<void (const A1 &, const
{
}
+ SignalWatch2(const SignalFilter &filter)
+ : SignalWatch<Callback_t>(filter)
+ {}
+
static void internalCallback(GDBusConnection *conn,
const gchar *sender,
const gchar *path,
@@ -4669,15 +4958,20 @@ class SignalWatch2 : public SignalWatch< boost::function<void (const A1 &, const
gpointer data) throw ()
{
try {
- const Callback_t &cb = static_cast< SignalWatch<Callback_t> *>(data)->getCallback();
+ SignalWatch<Callback_t> *watch = static_cast< SignalWatch<Callback_t> *>(data);
+ ExtractArgs context(conn, sender, path, interface, signal);
+ if (!watch->matches(context)) {
+ return;
+ }
typename dbus_traits<A1>::host_type a1;
typename dbus_traits<A2>::host_type a2;
GVariantIter iter;
g_variant_iter_init(&iter, params);
- dbus_traits<A1>::get(conn, NULL, iter, a1);
- dbus_traits<A2>::get(conn, NULL, iter, a2);
+ dbus_traits<A1>::get(context, iter, a1);
+ dbus_traits<A2>::get(context, iter, a2);
+ const Callback_t &cb = watch->getCallback();
cb(a1, a2);
} catch (const std::exception &ex) {
g_error("unexpected exception caught in internalCallback(): %s", ex.what());
@@ -4706,6 +5000,10 @@ class SignalWatch3 : public SignalWatch< boost::function<void (const A1 &, const
{
}
+ SignalWatch3(const SignalFilter &filter)
+ : SignalWatch<Callback_t>(filter)
+ {}
+
static void internalCallback(GDBusConnection *conn,
const gchar *sender,
const gchar *path,
@@ -4715,7 +5013,11 @@ class SignalWatch3 : public SignalWatch< boost::function<void (const A1 &, const
gpointer data) throw ()
{
try {
- const Callback_t &cb =static_cast< SignalWatch<Callback_t> *>(data)->getCallback();
+ SignalWatch<Callback_t> *watch = static_cast< SignalWatch<Callback_t> *>(data);
+ ExtractArgs context(conn, sender, path, interface, signal);
+ if (!watch->matches(context)) {
+ return;
+ }
typename dbus_traits<A1>::host_type a1;
typename dbus_traits<A2>::host_type a2;
@@ -4723,9 +5025,10 @@ class SignalWatch3 : public SignalWatch< boost::function<void (const A1 &, const
GVariantIter iter;
g_variant_iter_init(&iter, params);
- dbus_traits<A1>::get(conn, NULL, iter, a1);
- dbus_traits<A2>::get(conn, NULL, iter, a2);
- dbus_traits<A3>::get(conn, NULL, iter, a3);
+ dbus_traits<A1>::get(context, iter, a1);
+ dbus_traits<A2>::get(context, iter, a2);
+ dbus_traits<A3>::get(context, iter, a3);
+ const Callback_t &cb = watch->getCallback();
cb(a1, a2, a3);
} catch (const std::exception &ex) {
g_error("unexpected exception caught in internalCallback(): %s", ex.what());
@@ -4757,6 +5060,10 @@ class SignalWatch4 : public SignalWatch< boost::function<void (const A1 &, const
{
}
+ SignalWatch4(const SignalFilter &filter)
+ : SignalWatch<Callback_t>(filter)
+ {}
+
static void internalCallback(GDBusConnection *conn,
const gchar *sender,
const gchar *path,
@@ -4766,7 +5073,11 @@ class SignalWatch4 : public SignalWatch< boost::function<void (const A1 &, const
gpointer data) throw ()
{
try {
- const Callback_t &cb = static_cast< SignalWatch<Callback_t> *>(data)->getCallback();
+ SignalWatch<Callback_t> *watch = static_cast< SignalWatch<Callback_t> *>(data);
+ ExtractArgs context(conn, sender, path, interface, signal);
+ if (!watch->matches(context)) {
+ return;
+ }
typename dbus_traits<A1>::host_type a1;
typename dbus_traits<A2>::host_type a2;
@@ -4775,10 +5086,11 @@ class SignalWatch4 : public SignalWatch< boost::function<void (const A1 &, const
GVariantIter iter;
g_variant_iter_init(&iter, params);
- dbus_traits<A1>::get(conn, NULL, iter, a1);
- dbus_traits<A2>::get(conn, NULL, iter, a2);
- dbus_traits<A3>::get(conn, NULL, iter, a3);
- dbus_traits<A4>::get(conn, NULL, iter, a4);
+ dbus_traits<A1>::get(context, iter, a1);
+ dbus_traits<A2>::get(context, iter, a2);
+ dbus_traits<A3>::get(context, iter, a3);
+ dbus_traits<A4>::get(context, iter, a4);
+ const Callback_t &cb = watch->getCallback();
cb(a1, a2, a3, a4);
} catch (const std::exception &ex) {
g_error("unexpected exception caught in internalCallback(): %s", ex.what());
@@ -4808,6 +5120,10 @@ class SignalWatch5 : public SignalWatch< boost::function<void (const A1 &, const
{
}
+ SignalWatch5(const SignalFilter &filter)
+ : SignalWatch<Callback_t>(filter)
+ {}
+
static void internalCallback(GDBusConnection *conn,
const gchar *sender,
const gchar *path,
@@ -4817,7 +5133,11 @@ class SignalWatch5 : public SignalWatch< boost::function<void (const A1 &, const
gpointer data)
{
try {
- const Callback_t &cb = static_cast< SignalWatch<Callback_t> *>(data)->getCallback();
+ SignalWatch<Callback_t> *watch = static_cast< SignalWatch<Callback_t> *>(data);
+ ExtractArgs context(conn, sender, path, interface, signal);
+ if (!watch->matches(context)) {
+ return;
+ }
typename dbus_traits<A1>::host_type a1;
typename dbus_traits<A2>::host_type a2;
@@ -4827,11 +5147,12 @@ class SignalWatch5 : public SignalWatch< boost::function<void (const A1 &, const
GVariantIter iter;
g_variant_iter_init(&iter, params);
- dbus_traits<A1>::get(conn, NULL, iter, a1);
- dbus_traits<A2>::get(conn, NULL, iter, a2);
- dbus_traits<A3>::get(conn, NULL, iter, a3);
- dbus_traits<A4>::get(conn, NULL, iter, a4);
- dbus_traits<A5>::get(conn, NULL, iter, a5);
+ dbus_traits<A1>::get(context, iter, a1);
+ dbus_traits<A2>::get(context, iter, a2);
+ dbus_traits<A3>::get(context, iter, a3);
+ dbus_traits<A4>::get(context, iter, a4);
+ dbus_traits<A5>::get(context, iter, a5);
+ const Callback_t &cb = watch->getCallback();
cb(a1, a2, a3, a4, a5);
} catch (const std::exception &ex) {
g_error("unexpected exception caught in internalCallback(): %s", ex.what());
@@ -4864,6 +5185,10 @@ class SignalWatch6 : public SignalWatch< boost::function<void (const A1 &, const
{
}
+ SignalWatch6(const SignalFilter &filter)
+ : SignalWatch<Callback_t>(filter)
+ {}
+
static void internalCallback(GDBusConnection *conn,
const gchar *sender,
const gchar *path,
@@ -4873,7 +5198,11 @@ class SignalWatch6 : public SignalWatch< boost::function<void (const A1 &, const
gpointer data) throw ()
{
try {
- const Callback_t &cb = static_cast< SignalWatch<Callback_t> *>(data)->getCallback();
+ SignalWatch<Callback_t> *watch = static_cast< SignalWatch<Callback_t> *>(data);
+ ExtractArgs context(conn, sender, path, interface, signal);
+ if (!watch->matches(context)) {
+ return;
+ }
typename dbus_traits<A1>::host_type a1;
typename dbus_traits<A2>::host_type a2;
@@ -4884,12 +5213,13 @@ class SignalWatch6 : public SignalWatch< boost::function<void (const A1 &, const
GVariantIter iter;
g_variant_iter_init(&iter, params);
- dbus_traits<A1>::get(conn, NULL, iter, a1);
- dbus_traits<A2>::get(conn, NULL, iter, a2);
- dbus_traits<A3>::get(conn, NULL, iter, a3);
- dbus_traits<A4>::get(conn, NULL, iter, a4);
- dbus_traits<A5>::get(conn, NULL, iter, a5);
- dbus_traits<A6>::get(conn, NULL, iter, a6);
+ dbus_traits<A1>::get(context, iter, a1);
+ dbus_traits<A2>::get(context, iter, a2);
+ dbus_traits<A3>::get(context, iter, a3);
+ dbus_traits<A4>::get(context, iter, a4);
+ dbus_traits<A5>::get(context, iter, a5);
+ dbus_traits<A6>::get(context, iter, a6);
+ const Callback_t &cb = watch->getCallback();
cb(a1, a2, a3, a4, a5, a6);
} catch (const std::exception &ex) {
g_error("unexpected exception caught in internalCallback(): %s", ex.what());
diff --git a/src/gdbusxx/gdbus-cxx.h b/src/gdbusxx/gdbus-cxx.h
index 42e49627..a8f31566 100644
--- a/src/gdbusxx/gdbus-cxx.h
+++ b/src/gdbusxx/gdbus-cxx.h
@@ -51,7 +51,7 @@ private:
};
/**
- * Special parameter type that identifies a caller. A string in practice.
+ * Special parameter type that identifies a D-Bus bus address. A string in practice.
*/
class Caller_t : public std::string
{
@@ -61,6 +61,40 @@ class Caller_t : public std::string
template <class T> Caller_t &operator = (T val) { assign(val); return *this; }
};
+/**
+ * Special parameter type that identifies the path in a D-Bus message. A string in practice.
+ */
+class Path_t : public std::string
+{
+ public:
+ Path_t() {}
+ template <class T> Path_t(T val) : std::string(val) {}
+ template <class T> Path_t &operator = (T val) { assign(val); return *this; }
+};
+
+/**
+ * Special parameter type that identifies the interface in a D-Bus message. A string in practice.
+ */
+class Interface_t : public std::string
+{
+ public:
+ Interface_t() {}
+ template <class T> Interface_t(T val) : std::string(val) {}
+ template <class T> Interface_t &operator = (T val) { assign(val); return *this; }
+};
+
+/**
+ * Special parameter type that identifies the member of an interface
+ * (= signal or method) in a D-Bus message. A string in practice.
+ */
+class Member_t : public std::string
+{
+ public:
+ Member_t() {}
+ template <class T> Member_t(T val) : std::string(val) {}
+ template <class T> Member_t &operator = (T val) { assign(val); return *this; }
+};
+
class Watch;
/**