- update the file NEWS based on the git history
+ - verify that the version number of dbus-specification.xml is
+ changed if it needs to be; if changes have been made, update the
+ release date in that file
+
- update the AUTHORS file with "make update-authors" if necessary
- the version number should have major.minor.micro, even
- bump the version number up in configure.ac (so the micro version is odd),
and commit it. Make sure you do this *after* tagging the previous
release! The idea is that git has a newer version number
- than anything released.
+ than anything released. Similarly, bump the version number of
+ dbus-specification.xml and set the release date to "(not finalized)".
- merge the branch you've released to the chronologically-later
branch (usually "master"). You'll probably have to fix a merge
-D-Bus 1.4.14 (UNRELEASED)
+D-Bus 1.5.6 (UNRELEASED)
==
-Changes:
+Potentially incompatible (Bustle and similar debugging tools will need
+changes to work as intended):
+
+• Do not allow match rules to "eavesdrop" (receive messages intended for a
+ different recipient) by mistake: eavesdroppers must now opt-in to this
+ behaviour by putting "eavesdrop='true'" in the match rule, which will
+ not have any practical effect on buses where eavesdropping is not allowed
+ (fd.o #37890, Cosimo Alfarano)
+
+Other changes:
• Use DBUS_ERROR_OBJECT_PATH_IN_USE if dbus_connection_try_register_object_path
- or dbus_connection_try_register_fallback fails, not ...ADDRESS_IN_USE
- (fd.o #38874, Jiří Klimeš)
+ or dbus_connection_try_register_fallback fails, not ...ADDRESS_IN_USE,
+ and simplify object-path registration (fd.o #38874, Jiří Klimeš)
• Consistently use atomic operations on the refcounts of DBusPendingCall
and DBusServer, as was done for DBusConnection in 1.4.12 (fd.o #38005,
• Make "make check" in a clean tree work, by not running tests until
test data has been set up (fd.o #34405, Simon McVittie)
+• The dbus-daemon no longer busy-loops if it has a very large number of file
+ descriptors (fd.o #23194, Simon McVittie)
+
+• Some cmake fixes (Ralf Habacker)
+
+• Remove dead code, mainly from DBusString (fd.o #38570, Simon McVittie)
+
+• Stop storing two extra byte order indicators in each D-Bus message
+ (fd.o #38287, Simon McVittie)
+
+• Add an optional Stats interface which can be used to get statistics from
+ a running dbus-daemon if enabled at configure time with --enable-stats
+ (fd.o #34040, Simon McVittie)
+
• Documentation (fd.o #36156, Simon McVittie):
· let xsltproc be overridden as usual: ./configure XSLTPROC=myxsltproc
· install more documentation automatically, including man2html output
· fix use of a mutex for autolaunch server detection
· don't crash on malloc failure in _dbus_printf_string_upper_bound
-D-Bus 1.4.12 (2011-06-10)
+D-Bus 1.5.4 (2011-06-10)
==
Security (local denial of service):
• Windows-specific changes:
· don't try to build dbus-daemon-launch-helper (fd.o #37838, Mark Brand)
-D-Bus 1.4.10 (2011-06-01)
+D-Bus 1.5.2 (2011-06-01)
==
-The "Ape Ale" release.
+The "Boar Hunter" release.
Notes for distributors:
Changes:
+ • D-Bus Specification v0.17
+ · Reserve the extra characters used in signatures by GVariant
+ (fd.o #34529, Simon McVittie)
+ · Define the ObjectManager interface (fd.o #34869, David Zeuthen)
• Don't force -fPIE: distributions and libtool know better than we do whether
it's desirable (fd.o #16621, fd.o #27215; Simon McVittie)
• Allow --disable-gc-sections, in case your toolchain offers the
(fd.o #14512; Simon McVittie, loosely based on a patch from Luca Barbato)
• Ensure that maintainers upload documentation with the right permissions
(fd.o #36130, Simon McVittie)
+ • Don't force users of libdbus to be linked against -lpthread, -lrt
+ (fd.o #32827, Simon McVittie)
• Log system-bus activation information to syslog (fd.o #35705,
Colin Walters)
• Log messages dropped due to quotas to syslog (fd.o #35358,
• Windows:
• Remove obsolete workaround for winioctl.h (fd.o #35083, Ralf Habacker)
+D-Bus 1.5.0 (2011-04-11)
+==
+
+The "you never know when you need to tow something from your giant
+flying shark" release.
+
+ • D-Bus Specification v0.16
+ · Add support for path_namespace and arg0namespace in match rules
+ (fd.o #24317, #34870; Will Thompson, David Zeuthen, Simon McVittie)
+ · Make argNpath support object paths, not just object-path-like strings,
+ and document it better (fd.o #31818, Will Thompson)
+ • Let the bus daemon implement more than one interface (fd.o #33757,
+ Simon McVittie)
+ • Optimize _dbus_string_replace_len to reduce waste (fd.o #21261,
+ Roberto Guido)
+ • Require user intervention to compile with missing 64-bit support
+ (fd.o #35114, Simon McVittie)
+ • Add dbus_type_is_valid as public API (fd.o #20496, Simon McVittie)
+ • Raise UnknownObject instead of UnknownMethod for calls to methods on
+ paths that are not part of the object tree, and UnknownInterface for calls
+ to unknown interfaces in the bus daemon (fd.o #34527, Lennart Poettering)
+
D-Bus 1.4.8 (2011-04-08)
==
services.h \
signals.c \
signals.h \
+ stats.c \
+ stats.h \
test.c \
test.h \
utils.c \
dbus_free (entry);
}
-static void
-handle_timeout_callback (DBusTimeout *timeout,
- void *data)
-{
- BusPendingActivation *pending_activation = data;
-
- while (!dbus_timeout_handle (pending_activation->timeout))
- _dbus_wait_for_memory ();
-}
-
static BusPendingActivation *
bus_pending_activation_ref (BusPendingActivation *pending_activation)
{
if (pending_activation->timeout_added)
{
_dbus_loop_remove_timeout (bus_context_get_loop (pending_activation->activation->context),
- pending_activation->timeout,
- handle_timeout_callback, pending_activation);
+ pending_activation->timeout);
pending_activation->timeout_added = FALSE;
}
}
}
-static dbus_bool_t
-babysitter_watch_callback (DBusWatch *watch,
- unsigned int condition,
- void *data)
+static void
+pending_activation_finished_cb (DBusBabysitter *babysitter,
+ void *data)
{
BusPendingActivation *pending_activation = data;
- dbus_bool_t retval;
- DBusBabysitter *babysitter;
dbus_bool_t uses_servicehelper;
- babysitter = pending_activation->babysitter;
-
+ _dbus_assert (babysitter == pending_activation->babysitter);
_dbus_babysitter_ref (babysitter);
- retval = dbus_watch_handle (watch, condition);
-
/* There are two major cases here; are we the system bus or the session? Here this
* is distinguished by whether or not we use a setuid helper launcher. With the launch helper,
* some process exit codes are meaningful, processed by handle_servicehelper_exit_error.
*/
uses_servicehelper = bus_context_get_servicehelper (pending_activation->activation->context) != NULL;
- /* FIXME this is broken in the same way that
- * connection watches used to be; there should be
- * a separate callback for status change, instead
- * of doing "if we handled a watch status might
- * have changed"
- *
- * Fixing this lets us move dbus_watch_handle
- * calls into dbus-mainloop.c
- */
+ /* strictly speaking this is redundant with the check in dbus-spawn now */
if (_dbus_babysitter_get_child_exited (babysitter))
{
DBusError error;
}
_dbus_babysitter_unref (babysitter);
-
- return retval;
}
static dbus_bool_t
{
BusPendingActivation *pending_activation = data;
- return _dbus_loop_add_watch (bus_context_get_loop (pending_activation->activation->context),
- watch, babysitter_watch_callback, pending_activation,
- NULL);
+ return _dbus_loop_add_watch (
+ bus_context_get_loop (pending_activation->activation->context),
+ watch);
}
static void
BusPendingActivation *pending_activation = data;
_dbus_loop_remove_watch (bus_context_get_loop (pending_activation->activation->context),
- watch, babysitter_watch_callback, pending_activation);
+ watch);
}
static dbus_bool_t
}
if (!_dbus_loop_add_timeout (bus_context_get_loop (activation->context),
- pending_activation->timeout,
- handle_timeout_callback,
- pending_activation,
- NULL))
+ pending_activation->timeout))
{
_dbus_verbose ("Failed to add timeout for pending activation\n");
_dbus_assert (pending_activation->babysitter != NULL);
+ _dbus_babysitter_set_result_function (pending_activation->babysitter,
+ pending_activation_finished_cb,
+ pending_activation);
+
if (!_dbus_babysitter_set_watch_functions (pending_activation->babysitter,
add_babysitter_watch,
remove_babysitter_watch,
}
static dbus_bool_t
-server_watch_callback (DBusWatch *watch,
- unsigned int condition,
- void *data)
-{
- /* FIXME this can be done in dbus-mainloop.c
- * if the code in activation.c for the babysitter
- * watch handler is fixed.
- */
-
- return dbus_watch_handle (watch, condition);
-}
-
-static dbus_bool_t
add_server_watch (DBusWatch *watch,
void *data)
{
context = server_get_context (server);
- return _dbus_loop_add_watch (context->loop,
- watch, server_watch_callback, server,
- NULL);
+ return _dbus_loop_add_watch (context->loop, watch);
}
static void
context = server_get_context (server);
- _dbus_loop_remove_watch (context->loop,
- watch, server_watch_callback, server);
-}
-
-
-static void
-server_timeout_callback (DBusTimeout *timeout,
- void *data)
-{
- /* can return FALSE on OOM but we just let it fire again later */
- dbus_timeout_handle (timeout);
+ _dbus_loop_remove_watch (context->loop, watch);
}
static dbus_bool_t
context = server_get_context (server);
- return _dbus_loop_add_timeout (context->loop,
- timeout, server_timeout_callback, server, NULL);
+ return _dbus_loop_add_timeout (context->loop, timeout);
}
static void
context = server_get_context (server);
- _dbus_loop_remove_timeout (context->loop,
- timeout, server_timeout_callback, server);
+ _dbus_loop_remove_timeout (context->loop, timeout);
}
static void
DBusTimeout *expire_timeout; /**< Timeout for expiring incomplete connections. */
int stamp; /**< Incrementing number */
BusExpireList *pending_replies; /**< List of pending replies */
+
+#ifdef DBUS_ENABLE_STATS
+ int total_match_rules;
+ int peak_match_rules;
+ int peak_match_rules_per_conn;
+
+ int total_bus_names;
+ int peak_bus_names;
+ int peak_bus_names_per_conn;
+#endif
};
static dbus_int32_t connection_data_slot = -1;
long connection_tv_sec; /**< Time when we connected (seconds component) */
long connection_tv_usec; /**< Time when we connected (microsec component) */
int stamp; /**< connections->stamp last time we were traversed */
+
+#ifdef DBUS_ENABLE_STATS
+ int peak_match_rules;
+ int peak_bus_names;
+#endif
} BusConnectionData;
static dbus_bool_t bus_pending_reply_expired (BusExpireList *list,
}
static dbus_bool_t
-connection_watch_callback (DBusWatch *watch,
- unsigned int condition,
- void *data)
-{
- /* FIXME this can be done in dbus-mainloop.c
- * if the code in activation.c for the babysitter
- * watch handler is fixed.
- */
-
-#if 0
- _dbus_verbose ("Calling handle_watch\n");
-#endif
- return dbus_watch_handle (watch, condition);
-}
-
-static dbus_bool_t
add_connection_watch (DBusWatch *watch,
void *data)
{
DBusConnection *connection = data;
- return _dbus_loop_add_watch (connection_get_loop (connection),
- watch, connection_watch_callback, connection,
- NULL);
+ return _dbus_loop_add_watch (connection_get_loop (connection), watch);
}
static void
{
DBusConnection *connection = data;
- _dbus_loop_remove_watch (connection_get_loop (connection),
- watch, connection_watch_callback, connection);
-}
-
-static void
-connection_timeout_callback (DBusTimeout *timeout,
- void *data)
-{
- /* DBusConnection *connection = data; */
-
- /* can return FALSE on OOM but we just let it fire again later */
- dbus_timeout_handle (timeout);
+ _dbus_loop_remove_watch (connection_get_loop (connection), watch);
}
static dbus_bool_t
{
DBusConnection *connection = data;
- return _dbus_loop_add_timeout (connection_get_loop (connection),
- timeout, connection_timeout_callback, connection, NULL);
+ return _dbus_loop_add_timeout (connection_get_loop (connection), timeout);
}
static void
{
DBusConnection *connection = data;
- _dbus_loop_remove_timeout (connection_get_loop (connection),
- timeout, connection_timeout_callback, connection);
+ _dbus_loop_remove_timeout (connection_get_loop (connection), timeout);
}
static void
goto failed_4;
if (!_dbus_loop_add_timeout (bus_context_get_loop (context),
- connections->expire_timeout,
- call_timeout_callback, NULL, NULL))
+ connections->expire_timeout))
goto failed_5;
connections->refcount = 1;
bus_expire_list_free (connections->pending_replies);
_dbus_loop_remove_timeout (bus_context_get_loop (connections->context),
- connections->expire_timeout,
- call_timeout_callback, NULL);
+ connections->expire_timeout);
_dbus_timeout_unref (connections->expire_timeout);
d->oom_preallocated = NULL;
}
+#ifdef DBUS_ENABLE_STATS
+static void
+update_peak (int *peak,
+ int n)
+{
+ if (*peak < n)
+ *peak = n;
+}
+#endif
+
void
bus_connection_add_match_rule_link (DBusConnection *connection,
DBusList *link)
_dbus_list_append_link (&d->match_rules, link);
d->n_match_rules += 1;
+
+#ifdef DBUS_ENABLE_STATS
+ update_peak (&d->peak_match_rules, d->n_match_rules);
+ update_peak (&d->connections->peak_match_rules_per_conn, d->n_match_rules);
+
+ d->connections->total_match_rules += 1;
+ update_peak (&d->connections->peak_match_rules,
+ d->connections->total_match_rules);
+#endif
}
dbus_bool_t
d->n_match_rules -= 1;
_dbus_assert (d->n_match_rules >= 0);
+
+#ifdef DBUS_ENABLE_STATS
+ d->connections->total_match_rules -= 1;
+#endif
}
int
_dbus_list_append_link (&d->services_owned, link);
d->n_services_owned += 1;
+
+#ifdef DBUS_ENABLE_STATS
+ update_peak (&d->peak_bus_names, d->n_services_owned);
+ update_peak (&d->connections->peak_bus_names_per_conn,
+ d->n_services_owned);
+
+ d->connections->total_bus_names += 1;
+ update_peak (&d->connections->peak_bus_names,
+ d->connections->total_bus_names);
+#endif
}
dbus_bool_t
d->n_services_owned -= 1;
_dbus_assert (d->n_services_owned >= 0);
+
+#ifdef DBUS_ENABLE_STATS
+ d->connections->total_bus_names -= 1;
+#endif
}
int
return TRUE;
}
+
+#ifdef DBUS_ENABLE_STATS
+int
+bus_connections_get_n_active (BusConnections *connections)
+{
+ return connections->n_completed;
+}
+
+int
+bus_connections_get_n_incomplete (BusConnections *connections)
+{
+ return connections->n_incomplete;
+}
+
+int
+bus_connections_get_total_match_rules (BusConnections *connections)
+{
+ return connections->total_match_rules;
+}
+
+int
+bus_connections_get_peak_match_rules (BusConnections *connections)
+{
+ return connections->peak_match_rules;
+}
+
+int
+bus_connections_get_peak_match_rules_per_conn (BusConnections *connections)
+{
+ return connections->peak_match_rules_per_conn;
+}
+
+int
+bus_connections_get_total_bus_names (BusConnections *connections)
+{
+ return connections->total_bus_names;
+}
+
+int
+bus_connections_get_peak_bus_names (BusConnections *connections)
+{
+ return connections->peak_bus_names;
+}
+
+int
+bus_connections_get_peak_bus_names_per_conn (BusConnections *connections)
+{
+ return connections->peak_bus_names_per_conn;
+}
+
+int
+bus_connection_get_peak_match_rules (DBusConnection *connection)
+{
+ BusConnectionData *d;
+
+ d = BUS_CONNECTION_DATA (connection);
+ return d->peak_match_rules;
+}
+
+int
+bus_connection_get_peak_bus_names (DBusConnection *connection)
+{
+ BusConnectionData *d;
+
+ d = BUS_CONNECTION_DATA (connection);
+ return d->peak_bus_names;
+}
+#endif /* DBUS_ENABLE_STATS */
void *data,
DBusFreeFunction free_data_function);
+/* called by stats.c, only present if DBUS_ENABLE_STATS */
+int bus_connections_get_n_active (BusConnections *connections);
+int bus_connections_get_n_incomplete (BusConnections *connections);
+int bus_connections_get_total_match_rules (BusConnections *connections);
+int bus_connections_get_peak_match_rules (BusConnections *connections);
+int bus_connections_get_peak_match_rules_per_conn (BusConnections *connections);
+int bus_connections_get_total_bus_names (BusConnections *connections);
+int bus_connections_get_peak_bus_names (BusConnections *connections);
+int bus_connections_get_peak_bus_names_per_conn (BusConnections *connections);
+
+int bus_connection_get_peak_match_rules (DBusConnection *connection);
+int bus_connection_get_peak_bus_names (DBusConnection *connection);
+
#endif /* BUS_CONNECTION_H */
static DBusLoop *loop = NULL;
static dbus_bool_t
-_inotify_watch_callback (DBusWatch *watch, unsigned int condition, void *data)
-{
- return dbus_watch_handle (watch, condition);
-}
-
-static dbus_bool_t
_handle_inotify_watch (DBusWatch *passed_watch, unsigned int flags, void *data)
{
char buffer[INOTIFY_BUF_LEN];
_set_watched_dirs_internal (&empty);
- close (inotify_fd);
- inotify_fd = -1;
if (watch != NULL)
{
- _dbus_loop_remove_watch (loop, watch, _inotify_watch_callback, NULL);
+ _dbus_loop_remove_watch (loop, watch);
+ _dbus_watch_invalidate (watch);
_dbus_watch_unref (watch);
_dbus_loop_unref (loop);
}
watch = NULL;
loop = NULL;
+
+ close (inotify_fd);
+ inotify_fd = -1;
}
static int
goto out;
}
- if (!_dbus_loop_add_watch (loop, watch, _inotify_watch_callback,
- NULL, NULL))
+ if (!_dbus_loop_add_watch (loop, watch))
{
_dbus_warn ("Unable to add reload watch to main loop");
_dbus_watch_unref (watch);
static DBusLoop *loop = NULL;
static dbus_bool_t
-_kqueue_watch_callback (DBusWatch *watch, unsigned int condition, void *data)
-{
- return dbus_watch_handle (watch, condition);
-}
-
-static dbus_bool_t
_handle_kqueue_watch (DBusWatch *watch, unsigned int flags, void *data)
{
struct kevent ev;
kq = -1;
if (watch != NULL)
{
- _dbus_loop_remove_watch (loop, watch, _kqueue_watch_callback, NULL);
+ _dbus_loop_remove_watch (loop, watch);
+ _dbus_watch_invalidate (watch);
_dbus_watch_unref (watch);
watch = NULL;
}
goto out;
}
- if (!_dbus_loop_add_watch (loop, watch, _kqueue_watch_callback,
- NULL, NULL))
+ if (!_dbus_loop_add_watch (loop, watch))
{
_dbus_warn ("Unable to add reload watch to main loop");
- close (kq);
- kq = -1;
+ _dbus_watch_invalidate (watch);
_dbus_watch_unref (watch);
watch = NULL;
+ close (kq);
+ kq = -1;
goto out;
}
}
#include "services.h"
#include "selinux.h"
#include "signals.h"
+#include "stats.h"
#include "utils.h"
#include <dbus/dbus-string.h>
#include <dbus/dbus-internals.h>
return FALSE;
}
-/* For speed it might be useful to sort this in order of
- * frequency of use (but doesn't matter with only a few items
- * anyhow)
- */
-static struct
+typedef struct
{
const char *name;
const char *in_args;
BusTransaction *transaction,
DBusMessage *message,
DBusError *error);
-} message_handlers[] = {
+} MessageHandler;
+
+/* For speed it might be useful to sort this in order of
+ * frequency of use (but doesn't matter with only a few items
+ * anyhow)
+ */
+static const MessageHandler dbus_message_handlers[] = {
{ "Hello",
"",
DBUS_TYPE_STRING_AS_STRING,
{ "GetId",
"",
DBUS_TYPE_STRING_AS_STRING,
- bus_driver_handle_get_id }
+ bus_driver_handle_get_id },
+ { NULL, NULL, NULL, NULL }
+};
+
+static dbus_bool_t bus_driver_handle_introspect (DBusConnection *,
+ BusTransaction *, DBusMessage *, DBusError *);
+
+static const MessageHandler introspectable_message_handlers[] = {
+ { "Introspect", "", DBUS_TYPE_STRING_AS_STRING, bus_driver_handle_introspect },
+ { NULL, NULL, NULL, NULL }
+};
+
+#ifdef DBUS_ENABLE_STATS
+static const MessageHandler stats_message_handlers[] = {
+ { "GetStats", "", "a{sv}", bus_stats_handle_get_stats },
+ { "GetConnectionStats", "s", "a{sv}", bus_stats_handle_get_connection_stats },
+ { NULL, NULL, NULL, NULL }
+};
+#endif
+
+typedef struct {
+ const char *name;
+ const MessageHandler *message_handlers;
+ const char *extra_introspection;
+} InterfaceHandler;
+
+/* These should ideally be sorted by frequency of use, although it
+ * probably doesn't matter with this few items */
+static InterfaceHandler interface_handlers[] = {
+ { DBUS_INTERFACE_DBUS, dbus_message_handlers,
+ " <signal name=\"NameOwnerChanged\">\n"
+ " <arg type=\"s\"/>\n"
+ " <arg type=\"s\"/>\n"
+ " <arg type=\"s\"/>\n"
+ " </signal>\n"
+ " <signal name=\"NameLost\">\n"
+ " <arg type=\"s\"/>\n"
+ " </signal>\n"
+ " <signal name=\"NameAcquired\">\n"
+ " <arg type=\"s\"/>\n"
+ " </signal>\n" },
+ { DBUS_INTERFACE_INTROSPECTABLE, introspectable_message_handlers, NULL },
+#ifdef DBUS_ENABLE_STATS
+ { BUS_INTERFACE_STATS, stats_message_handlers, NULL },
+#endif
+ { NULL, NULL, NULL }
};
static dbus_bool_t
dbus_bool_t
bus_driver_generate_introspect_string (DBusString *xml)
{
- int i;
+ const InterfaceHandler *ih;
+ const MessageHandler *mh;
if (!_dbus_string_append (xml, DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE))
return FALSE;
if (!_dbus_string_append (xml, "<node>\n"))
return FALSE;
- if (!_dbus_string_append_printf (xml, " <interface name=\"%s\">\n", DBUS_INTERFACE_INTROSPECTABLE))
- return FALSE;
- if (!_dbus_string_append (xml, " <method name=\"Introspect\">\n"))
- return FALSE;
- if (!_dbus_string_append_printf (xml, " <arg name=\"data\" direction=\"out\" type=\"%s\"/>\n", DBUS_TYPE_STRING_AS_STRING))
- return FALSE;
- if (!_dbus_string_append (xml, " </method>\n"))
- return FALSE;
- if (!_dbus_string_append (xml, " </interface>\n"))
- return FALSE;
- if (!_dbus_string_append_printf (xml, " <interface name=\"%s\">\n",
- DBUS_INTERFACE_DBUS))
- return FALSE;
-
- i = 0;
- while (i < _DBUS_N_ELEMENTS (message_handlers))
+ for (ih = interface_handlers; ih->name != NULL; ih++)
{
-
- if (!_dbus_string_append_printf (xml, " <method name=\"%s\">\n",
- message_handlers[i].name))
+ if (!_dbus_string_append_printf (xml, " <interface name=\"%s\">\n",
+ ih->name))
return FALSE;
- if (!write_args_for_direction (xml, message_handlers[i].in_args, TRUE))
- return FALSE;
-
- if (!write_args_for_direction (xml, message_handlers[i].out_args, FALSE))
- return FALSE;
-
- if (!_dbus_string_append (xml, " </method>\n"))
- return FALSE;
-
- ++i;
- }
-
- if (!_dbus_string_append_printf (xml, " <signal name=\"NameOwnerChanged\">\n"))
- return FALSE;
-
- if (!_dbus_string_append_printf (xml, " <arg type=\"s\"/>\n"))
- return FALSE;
-
- if (!_dbus_string_append_printf (xml, " <arg type=\"s\"/>\n"))
- return FALSE;
-
- if (!_dbus_string_append_printf (xml, " <arg type=\"s\"/>\n"))
- return FALSE;
-
- if (!_dbus_string_append_printf (xml, " </signal>\n"))
- return FALSE;
-
-
-
- if (!_dbus_string_append_printf (xml, " <signal name=\"NameLost\">\n"))
- return FALSE;
-
- if (!_dbus_string_append_printf (xml, " <arg type=\"s\"/>\n"))
- return FALSE;
-
- if (!_dbus_string_append_printf (xml, " </signal>\n"))
- return FALSE;
-
+ for (mh = ih->message_handlers; mh->name != NULL; mh++)
+ {
+ if (!_dbus_string_append_printf (xml, " <method name=\"%s\">\n",
+ mh->name))
+ return FALSE;
+ if (!write_args_for_direction (xml, mh->in_args, TRUE))
+ return FALSE;
- if (!_dbus_string_append_printf (xml, " <signal name=\"NameAcquired\">\n"))
- return FALSE;
+ if (!write_args_for_direction (xml, mh->out_args, FALSE))
+ return FALSE;
- if (!_dbus_string_append_printf (xml, " <arg type=\"s\"/>\n"))
- return FALSE;
+ if (!_dbus_string_append (xml, " </method>\n"))
+ return FALSE;
+ }
- if (!_dbus_string_append_printf (xml, " </signal>\n"))
- return FALSE;
+ if (ih->extra_introspection != NULL &&
+ !_dbus_string_append (xml, ih->extra_introspection))
+ return FALSE;
- if (!_dbus_string_append (xml, " </interface>\n"))
- return FALSE;
+ if (!_dbus_string_append (xml, " </interface>\n"))
+ return FALSE;
+ }
if (!_dbus_string_append (xml, "</node>\n"))
return FALSE;
DBusError *error)
{
const char *name, *sender, *interface;
- int i;
+ const InterfaceHandler *ih;
+ const MessageHandler *mh;
+ dbus_bool_t found_interface = FALSE;
_DBUS_ASSERT_ERROR_IS_CLEAR (error);
return TRUE; /* we just ignore this */
}
- if (dbus_message_is_method_call (message,
- DBUS_INTERFACE_INTROSPECTABLE,
- "Introspect"))
- return bus_driver_handle_introspect (connection, transaction, message, error);
-
+ /* may be NULL, which means "any interface will do" */
interface = dbus_message_get_interface (message);
- if (interface == NULL)
- interface = DBUS_INTERFACE_DBUS;
_dbus_assert (dbus_message_get_member (message) != NULL);
name = dbus_message_get_member (message);
sender = dbus_message_get_sender (message);
- if (strcmp (interface,
- DBUS_INTERFACE_DBUS) != 0)
- {
- _dbus_verbose ("Driver got message to unknown interface \"%s\"\n",
- interface);
- goto unknown;
- }
-
_dbus_verbose ("Driver got a method call: %s\n",
dbus_message_get_member (message));
/* security checks should have kept this from getting here */
_dbus_assert (sender != NULL || strcmp (name, "Hello") == 0);
- i = 0;
- while (i < _DBUS_N_ELEMENTS (message_handlers))
+ for (ih = interface_handlers; ih->name != NULL; ih++)
{
- if (strcmp (message_handlers[i].name, name) == 0)
+ if (interface != NULL && strcmp (interface, ih->name) != 0)
+ continue;
+
+ found_interface = TRUE;
+
+ for (mh = ih->message_handlers; mh->name != NULL; mh++)
{
+ if (strcmp (mh->name, name) != 0)
+ continue;
+
_dbus_verbose ("Found driver handler for %s\n", name);
- if (!dbus_message_has_signature (message, message_handlers[i].in_args))
+ if (!dbus_message_has_signature (message, mh->in_args))
{
_DBUS_ASSERT_ERROR_IS_CLEAR (error);
_dbus_verbose ("Call to %s has wrong args (%s, expected %s)\n",
name, dbus_message_get_signature (message),
- message_handlers[i].in_args);
+ mh->in_args);
dbus_set_error (error, DBUS_ERROR_INVALID_ARGS,
"Call to %s has wrong args (%s, expected %s)\n",
name, dbus_message_get_signature (message),
- message_handlers[i].in_args);
+ mh->in_args);
_DBUS_ASSERT_ERROR_IS_SET (error);
return FALSE;
}
- if ((* message_handlers[i].handler) (connection, transaction, message, error))
+ if ((* mh->handler) (connection, transaction, message, error))
{
_DBUS_ASSERT_ERROR_IS_CLEAR (error);
_dbus_verbose ("Driver handler succeeded\n");
return FALSE;
}
}
-
- ++i;
}
unknown:
_dbus_verbose ("No driver handler for message \"%s\"\n",
name);
- dbus_set_error (error, DBUS_ERROR_UNKNOWN_METHOD,
+ dbus_set_error (error, found_interface ? DBUS_ERROR_UNKNOWN_METHOD : DBUS_ERROR_UNKNOWN_INTERFACE,
"%s does not understand message %s",
DBUS_SERVICE_DBUS, name);
static dbus_bool_t expire_timeout_handler (void *data);
-static void
-call_timeout_callback (DBusTimeout *timeout,
- void *data)
-{
- /* can return FALSE on OOM but we just let it fire again later */
- dbus_timeout_handle (timeout);
-}
-
BusExpireList*
bus_expire_list_new (DBusLoop *loop,
int expire_after,
_dbus_timeout_set_enabled (list->timeout, FALSE);
- if (!_dbus_loop_add_timeout (list->loop,
- list->timeout,
- call_timeout_callback, NULL, NULL))
+ if (!_dbus_loop_add_timeout (list->loop, list->timeout))
goto failed;
return list;
{
_dbus_assert (list->items == NULL);
- _dbus_loop_remove_timeout (list->loop, list->timeout,
- call_timeout_callback, NULL);
+ _dbus_loop_remove_timeout (list->loop, list->timeout);
_dbus_timeout_unref (list->timeout);
#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h> /* for write() and STDERR_FILENO */
+#endif
#include "selinux.h"
static BusContext *context;
#define RELOAD_READ_END 0
#define RELOAD_WRITE_END 1
-static void close_reload_pipe (void);
+static void close_reload_pipe (DBusWatch **);
+#ifdef DBUS_UNIX
static void
signal_handler (int sig)
{
if ((reload_pipe[RELOAD_WRITE_END] > 0) &&
!_dbus_write_socket (reload_pipe[RELOAD_WRITE_END], &str, 0, 1))
{
- _dbus_warn ("Unable to write to reload pipe.\n");
- close_reload_pipe ();
+ /* If we receive SIGHUP often enough to fill the pipe buffer (4096
+ * times on old Linux, 65536 on modern Linux) before it can be
+ * drained, let's just warn and ignore. The configuration will be
+ * reloaded while draining the pipe buffer, which is what we
+ * wanted. It's harmless that it will be reloaded fewer times than
+ * we asked for, since the reload is delayed anyway, so new changes
+ * will be picked up.
+ *
+ * We use write() because _dbus_warn uses vfprintf, which isn't
+ * async-signal-safe.
+ *
+ * This is necessarily Unix-specific, but so are POSIX signals,
+ * so... */
+ static const char message[] =
+ "Unable to write to reload pipe - buffer full?\n";
+
+ write (STDERR_FILENO, message, strlen (message));
}
}
break;
#endif
}
}
+#endif /* DBUS_UNIX */
static void
usage (void)
_dbus_read_socket (reload_pipe[RELOAD_READ_END], &str, 1) != 1)
{
_dbus_warn ("Couldn't read from reload pipe.\n");
- close_reload_pipe ();
+ close_reload_pipe (&watch);
return TRUE;
}
_dbus_string_free (&str);
return TRUE;
}
-static dbus_bool_t
-reload_watch_callback (DBusWatch *watch,
- unsigned int condition,
- void *data)
-{
- return dbus_watch_handle (watch, condition);
-}
-
static void
setup_reload_pipe (DBusLoop *loop)
{
exit (1);
}
- if (!_dbus_loop_add_watch (loop, watch, reload_watch_callback,
- NULL, NULL))
+ if (!_dbus_loop_add_watch (loop, watch))
{
_dbus_warn ("Unable to add reload watch to main loop: %s\n",
error.message);
}
static void
-close_reload_pipe (void)
+close_reload_pipe (DBusWatch **watch)
{
+ _dbus_loop_remove_watch (bus_context_get_loop (context), *watch);
+ _dbus_watch_invalidate (*watch);
+ _dbus_watch_unref (*watch);
+ *watch = NULL;
+
_dbus_close_socket (reload_pipe[RELOAD_READ_END], NULL);
reload_pipe[RELOAD_READ_END] = -1;
setup_reload_pipe (bus_context_get_loop (context));
+#ifdef DBUS_UNIX
+ /* POSIX signals are Unix-specific, and _dbus_set_signal_handler is
+ * unimplemented (and probably unimplementable) on Windows, so there's
+ * no point in trying to make the handler portable to non-Unix. */
#ifdef SIGHUP
_dbus_set_signal_handler (SIGHUP, signal_handler);
#endif
#ifdef DBUS_BUS_ENABLE_DNOTIFY_ON_LINUX
_dbus_set_signal_handler (SIGIO, signal_handler);
#endif /* DBUS_BUS_ENABLE_DNOTIFY_ON_LINUX */
+#endif /* DBUS_UNIX */
_dbus_verbose ("We are on D-Bus...\n");
_dbus_loop_run (bus_context_get_loop (context));
int args_len;
};
+#define BUS_MATCH_ARG_NAMESPACE 0x4000000u
#define BUS_MATCH_ARG_IS_PATH 0x8000000u
+#define BUS_MATCH_ARG_FLAGS (BUS_MATCH_ARG_NAMESPACE | BUS_MATCH_ARG_IS_PATH)
+
BusMatchRule*
bus_match_rule_new (DBusConnection *matches_go_to)
{
if (rule->flags & BUS_MATCH_MESSAGE_TYPE)
{
- if (rule->message_type == DBUS_MESSAGE_TYPE_INVALID)
- {
- if (!_dbus_string_append_printf (&str, "type='INVALID'"))
- goto nomem;
- }
- else if (rule->message_type == DBUS_MESSAGE_TYPE_METHOD_CALL)
- {
- if (!_dbus_string_append_printf (&str, "type='method_call'"))
- goto nomem;
- }
- else if (rule->message_type == DBUS_MESSAGE_TYPE_METHOD_RETURN)
- {
- if (!_dbus_string_append_printf (&str, "type='method_return'"))
- goto nomem;
- }
- else if (rule->message_type == DBUS_MESSAGE_TYPE_ERROR)
- {
- if (!_dbus_string_append_printf (&str, "type='error'"))
- goto nomem;
- }
- else if (rule->message_type == DBUS_MESSAGE_TYPE_SIGNAL)
- {
- if (!_dbus_string_append_printf (&str, "type='signal'"))
- goto nomem;
- }
- else
- {
- if (!_dbus_string_append_printf (&str, "type='%d'", rule->message_type))
- goto nomem;
- }
+ if (!_dbus_string_append_printf (&str, "type='%s'",
+ dbus_message_type_to_string (rule->message_type)))
+ goto nomem;
}
if (rule->flags & BUS_MATCH_INTERFACE)
goto nomem;
}
+ if (rule->flags & BUS_MATCH_PATH_NAMESPACE)
+ {
+ if (_dbus_string_get_length (&str) > 0)
+ {
+ if (!_dbus_string_append (&str, ","))
+ goto nomem;
+ }
+
+ if (!_dbus_string_append_printf (&str, "path_namespace='%s'", rule->path))
+ goto nomem;
+ }
+
if (rule->flags & BUS_MATCH_SENDER)
{
if (_dbus_string_get_length (&str) > 0)
goto nomem;
}
+ if (rule->flags & BUS_MATCH_CLIENT_IS_EAVESDROPPING)
+ {
+ if (_dbus_string_get_length (&str) > 0)
+ {
+ if (!_dbus_string_append (&str, ","))
+ goto nomem;
+ }
+
+ if (!_dbus_string_append_printf (&str, "eavesdrop='%s'",
+ (rule->flags & BUS_MATCH_CLIENT_IS_EAVESDROPPING) ?
+ "true" : "false"))
+ goto nomem;
+ }
+
if (rule->flags & BUS_MATCH_ARGS)
{
int i;
{
if (rule->args[i] != NULL)
{
- dbus_bool_t is_path;
+ dbus_bool_t is_path, is_namespace;
if (_dbus_string_get_length (&str) > 0)
{
}
is_path = (rule->arg_lens[i] & BUS_MATCH_ARG_IS_PATH) != 0;
+ is_namespace = (rule->arg_lens[i] & BUS_MATCH_ARG_NAMESPACE) != 0;
if (!_dbus_string_append_printf (&str,
"arg%d%s='%s'",
- i, is_path ? "path" : "",
+ i,
+ is_path ? "path" :
+ is_namespace ? "namespace" : "",
rule->args[i]))
goto nomem;
}
return TRUE;
}
+void
+bus_match_rule_set_client_is_eavesdropping (BusMatchRule *rule,
+ dbus_bool_t is_eavesdropping)
+{
+ if (is_eavesdropping)
+ rule->flags |= BUS_MATCH_CLIENT_IS_EAVESDROPPING;
+ else
+ rule->flags &= ~(BUS_MATCH_CLIENT_IS_EAVESDROPPING);
+}
+
dbus_bool_t
bus_match_rule_set_path (BusMatchRule *rule,
- const char *path)
+ const char *path,
+ dbus_bool_t is_namespace)
{
char *new;
if (new == NULL)
return FALSE;
- rule->flags |= BUS_MATCH_PATH;
+ rule->flags &= ~(BUS_MATCH_PATH|BUS_MATCH_PATH_NAMESPACE);
+
+ if (is_namespace)
+ rule->flags |= BUS_MATCH_PATH_NAMESPACE;
+ else
+ rule->flags |= BUS_MATCH_PATH;
+
dbus_free (rule->path);
rule->path = new;
bus_match_rule_set_arg (BusMatchRule *rule,
int arg,
const DBusString *value,
- dbus_bool_t is_path)
+ dbus_bool_t is_path,
+ dbus_bool_t is_namespace)
{
int length;
char *new;
if (is_path)
rule->arg_lens[arg] |= BUS_MATCH_ARG_IS_PATH;
+ if (is_namespace)
+ rule->arg_lens[arg] |= BUS_MATCH_ARG_NAMESPACE;
+
/* NULL termination didn't get busted */
_dbus_assert (rule->args[rule->args_len] == NULL);
_dbus_assert (rule->arg_lens[rule->args_len] == 0);
const DBusString *value,
DBusError *error)
{
- dbus_bool_t is_path;
+ dbus_bool_t is_path = FALSE;
+ dbus_bool_t is_namespace = FALSE;
DBusString key_str;
unsigned long arg;
int length;
goto failed;
}
- if (end != length &&
- ((end + 4) != length ||
- !_dbus_string_ends_with_c_str (&key_str, "path")))
+ if (end != length)
{
- dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID,
- "Key '%s' in match rule contains junk after argument number. Only 'path' is optionally valid ('arg0path' for example).\n", key);
- goto failed;
- }
+ if ((end + strlen ("path")) == length &&
+ _dbus_string_ends_with_c_str (&key_str, "path"))
+ {
+ is_path = TRUE;
+ }
+ else if (_dbus_string_equal_c_str (&key_str, "arg0namespace"))
+ {
+ int value_len = _dbus_string_get_length (value);
- is_path = end != length;
+ is_namespace = TRUE;
+
+ if (!_dbus_validate_bus_namespace (value, 0, value_len))
+ {
+ dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID,
+ "arg0namespace='%s' is not a valid prefix of a bus name",
+ _dbus_string_get_const_data (value));
+ goto failed;
+ }
+ }
+ else
+ {
+ dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID,
+ "Key '%s' in match rule contains junk after argument number (%u). Only 'arg%upath' (for example) or 'arg0namespace' are valid", key, arg, arg);
+ goto failed;
+ }
+ }
/* If we didn't check this we could allocate a huge amount of RAM */
if (arg > DBUS_MAXIMUM_MATCH_RULE_ARG_NUMBER)
goto failed;
}
- if (!bus_match_rule_set_arg (rule, arg, value, is_path))
+ if (!bus_match_rule_set_arg (rule, arg, value, is_path, is_namespace))
{
BUS_SET_OOM (error);
goto failed;
goto failed;
}
}
- else if (strcmp (key, "path") == 0)
+ else if (strcmp (key, "path") == 0 ||
+ strcmp (key, "path_namespace") == 0)
{
- if (rule->flags & BUS_MATCH_PATH)
+ dbus_bool_t is_namespace = (strcmp (key, "path_namespace") == 0);
+
+ if (rule->flags & (BUS_MATCH_PATH | BUS_MATCH_PATH_NAMESPACE))
{
dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID,
- "Key %s specified twice in match rule\n", key);
+ "path or path_namespace specified twice in match rule\n");
goto failed;
}
goto failed;
}
- if (!bus_match_rule_set_path (rule, value))
+ if (!bus_match_rule_set_path (rule, value, is_namespace))
{
BUS_SET_OOM (error);
goto failed;
goto failed;
}
}
+ else if (strcmp (key, "eavesdrop") == 0)
+ {
+ /* do not detect "eavesdrop" being used more than once in rule:
+ * 1) it's not possible, it's only in the flags
+ * 2) it might be used twice to disable eavesdropping when it's
+ * automatically added (eg dbus-monitor/bustle) */
+
+ /* we accept only "true|false" as possible values */
+ if ((strcmp (value, "true") == 0))
+ {
+ bus_match_rule_set_client_is_eavesdropping (rule, TRUE);
+ }
+ else if (strcmp (value, "false") == 0)
+ {
+ bus_match_rule_set_client_is_eavesdropping (rule, FALSE);
+ }
+ else
+ {
+ dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID,
+ "eavesdrop='%s' is invalid, "
+ "it should be 'true' or 'false'\n",
+ value);
+ goto failed;
+ }
+ }
else if (strncmp (key, "arg", 3) == 0)
{
if (!bus_match_rule_parse_arg_match (rule, key, &tmp_str, error))
if ((a->flags & BUS_MATCH_PATH) &&
strcmp (a->path, b->path) != 0)
return FALSE;
-
+
if ((a->flags & BUS_MATCH_INTERFACE) &&
strcmp (a->interface, b->interface) != 0)
return FALSE;
strcmp (a->destination, b->destination) != 0)
return FALSE;
+ /* we already compared the value of flags, and
+ * BUS_MATCH_CLIENT_IS_EAVESDROPPING does not have another struct member */
+
if (a->flags & BUS_MATCH_ARGS)
{
int i;
if (a->arg_lens[i] != b->arg_lens[i])
return FALSE;
- length = a->arg_lens[i] & ~BUS_MATCH_ARG_IS_PATH;
+ length = a->arg_lens[i] & ~BUS_MATCH_ARG_FLAGS;
if (a->args[i] != NULL)
{
}
static dbus_bool_t
+str_has_prefix (const char *str, const char *prefix)
+{
+ size_t prefix_len;
+ prefix_len = strlen (prefix);
+ if (strncmp (str, prefix, prefix_len) == 0)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+static dbus_bool_t
match_rule_matches (BusMatchRule *rule,
DBusConnection *sender,
DBusConnection *addressed_recipient,
DBusMessage *message,
BusMatchFlags already_matched)
{
+ dbus_bool_t wants_to_eavesdrop = FALSE;
int flags;
/* All features of the match rule are AND'd together,
/* Don't bother re-matching features we've already checked implicitly. */
flags = rule->flags & (~already_matched);
+ if (flags & BUS_MATCH_CLIENT_IS_EAVESDROPPING)
+ wants_to_eavesdrop = TRUE;
+
if (flags & BUS_MATCH_MESSAGE_TYPE)
{
_dbus_assert (rule->message_type != DBUS_MESSAGE_TYPE_INVALID);
}
}
+ /* Note: this part is relevant for eavesdropper rules:
+ * Two cases:
+ * 1) rule has a destination to be matched
+ * (flag BUS_MATCH_DESTINATION present). Rule will match if:
+ * - rule->destination matches the addressed_recipient
+ * AND
+ * - wants_to_eavesdrop=TRUE
+ *
+ * Note: (the case in which addressed_recipient is the actual rule owner
+ * is handled elsewere in dispatch.c:bus_dispatch_matches().
+ *
+ * 2) rule has no destination. Rule will match if:
+ * - message has no specified destination (ie broadcasts)
+ * (Note: this will rule out unicast method calls and unicast signals,
+ * fixing FDO#269748)
+ * OR
+ * - wants_to_eavesdrop=TRUE (destination-catch-all situation)
+ */
if (flags & BUS_MATCH_DESTINATION)
{
const char *destination;
destination = dbus_message_get_destination (message);
if (destination == NULL)
+ /* broadcast, but this rule specified a destination: no match */
+ return FALSE;
+
+ /* rule owner does not intend to eavesdrop: we'll deliver only msgs
+ * directed to it, NOT MATCHING */
+ if (!wants_to_eavesdrop)
return FALSE;
if (addressed_recipient == NULL)
if (!connection_is_primary_owner (addressed_recipient, rule->destination))
return FALSE;
}
+ } else { /* no destination in rule */
+ dbus_bool_t msg_is_broadcast;
+
+ _dbus_assert (rule->destination == NULL);
+
+ msg_is_broadcast = (dbus_message_get_destination (message) == NULL);
+
+ if (!wants_to_eavesdrop && !msg_is_broadcast)
+ return FALSE;
+
+ /* if we are here rule owner intends to eavesdrop
+ * OR
+ * message is being broadcasted */
}
if (flags & BUS_MATCH_PATH)
return FALSE;
}
+ if (flags & BUS_MATCH_PATH_NAMESPACE)
+ {
+ const char *path;
+ int len;
+
+ _dbus_assert (rule->path != NULL);
+
+ path = dbus_message_get_path (message);
+ if (path == NULL)
+ return FALSE;
+
+ if (!str_has_prefix (path, rule->path))
+ return FALSE;
+
+ len = strlen (rule->path);
+
+ /* Check that the actual argument is within the expected
+ * namespace, rather than just starting with that string,
+ * by checking that the matched prefix is followed by a '/'
+ * or the end of the path.
+ */
+ if (path[len] != '\0' && path[len] != '/')
+ return FALSE;
+ }
+
if (flags & BUS_MATCH_ARGS)
{
int i;
int current_type;
const char *expected_arg;
int expected_length;
- dbus_bool_t is_path;
+ dbus_bool_t is_path, is_namespace;
expected_arg = rule->args[i];
- expected_length = rule->arg_lens[i] & ~BUS_MATCH_ARG_IS_PATH;
+ expected_length = rule->arg_lens[i] & ~BUS_MATCH_ARG_FLAGS;
is_path = (rule->arg_lens[i] & BUS_MATCH_ARG_IS_PATH) != 0;
+ is_namespace = (rule->arg_lens[i] & BUS_MATCH_ARG_NAMESPACE) != 0;
current_type = dbus_message_iter_get_arg_type (&iter);
{
const char *actual_arg;
int actual_length;
-
- if (current_type != DBUS_TYPE_STRING)
+
+ if (current_type != DBUS_TYPE_STRING &&
+ (!is_path || current_type != DBUS_TYPE_OBJECT_PATH))
return FALSE;
actual_arg = NULL;
MIN (actual_length, expected_length)) != 0)
return FALSE;
}
+ else if (is_namespace)
+ {
+ if (expected_length > actual_length)
+ return FALSE;
+
+ /* If the actual argument doesn't start with the expected
+ * namespace, then we don't match.
+ */
+ if (memcmp (expected_arg, actual_arg, expected_length) != 0)
+ return FALSE;
+
+ if (expected_length < actual_length)
+ {
+ /* Check that the actual argument is within the expected
+ * namespace, rather than just starting with that string,
+ * by checking that the matched prefix ends in a '.'.
+ *
+ * This doesn't stop "foo.bar." matching "foo.bar..baz"
+ * which is an invalid namespace, but at some point the
+ * daemon can't cover up for broken services.
+ */
+ if (actual_arg[expected_length] != '.')
+ return FALSE;
+ }
+ /* otherwise we had an exact match. */
+ }
else
{
if (expected_length != actual_length ||
bus_match_rule_unref (rule);
}
-
+
+ rule = check_parse (TRUE, "arg7path='/foo'");
+ if (rule != NULL)
+ {
+ _dbus_assert (rule->flags = BUS_MATCH_ARGS);
+ _dbus_assert (rule->args != NULL);
+ _dbus_assert (rule->args_len == 8);
+ _dbus_assert (rule->args[7] != NULL);
+ _dbus_assert (rule->args[8] == NULL);
+ _dbus_assert (strcmp (rule->args[7], "/foo") == 0);
+ _dbus_assert ((rule->arg_lens[7] & BUS_MATCH_ARG_IS_PATH)
+ == BUS_MATCH_ARG_IS_PATH);
+
+ bus_match_rule_unref (rule);
+ }
+
+ /* Arg 0 namespace matches */
+ rule = check_parse (TRUE, "arg0namespace='foo'");
+ if (rule != NULL)
+ {
+ _dbus_assert (rule->flags == BUS_MATCH_ARGS);
+ _dbus_assert (rule->args != NULL);
+ _dbus_assert (rule->args_len == 1);
+ _dbus_assert (strcmp (rule->args[0], "foo") == 0);
+ _dbus_assert ((rule->arg_lens[0] & BUS_MATCH_ARG_NAMESPACE)
+ == BUS_MATCH_ARG_NAMESPACE);
+
+ bus_match_rule_unref (rule);
+ }
+
+ rule = check_parse (TRUE, "arg0namespace='foo.bar'");
+ if (rule != NULL)
+ {
+ _dbus_assert (rule->flags == BUS_MATCH_ARGS);
+ _dbus_assert (rule->args != NULL);
+ _dbus_assert (rule->args_len == 1);
+ _dbus_assert (strcmp (rule->args[0], "foo.bar") == 0);
+ _dbus_assert ((rule->arg_lens[0] & BUS_MATCH_ARG_NAMESPACE)
+ == BUS_MATCH_ARG_NAMESPACE);
+
+ bus_match_rule_unref (rule);
+ }
+
+ /* Only arg0namespace is supported. */
+ rule = check_parse (FALSE, "arg1namespace='foo'");
+ _dbus_assert (rule == NULL);
+
+ /* An empty string isn't a valid namespace prefix (you should just not
+ * specify this key at all).
+ */
+ rule = check_parse (FALSE, "arg0namespace=''");
+ _dbus_assert (rule == NULL);
+
+ /* Trailing periods aren't allowed (earlier versions of the arg0namespace
+ * spec allowed a single trailing period, which altered the semantics) */
+ rule = check_parse (FALSE, "arg0namespace='foo.'");
+ _dbus_assert (rule == NULL);
+
+ rule = check_parse (FALSE, "arg0namespace='foo.bar.'");
+ _dbus_assert (rule == NULL);
+
+ rule = check_parse (FALSE, "arg0namespace='foo..'");
+ _dbus_assert (rule == NULL);
+
+ rule = check_parse (FALSE, "arg0namespace='foo.bar..'");
+ _dbus_assert (rule == NULL);
+
/* Too-large argN */
rule = check_parse (FALSE, "arg300='foo'");
_dbus_assert (rule == NULL);
rule = check_parse (FALSE, "type='signal',type='method_call'");
_dbus_assert (rule == NULL);
+ rule = check_parse (TRUE, "path_namespace='/foo/bar'");
+ if (rule != NULL)
+ {
+ _dbus_assert (rule->flags == BUS_MATCH_PATH_NAMESPACE);
+ _dbus_assert (rule->path != NULL);
+ _dbus_assert (strcmp (rule->path, "/foo/bar") == 0);
+
+ bus_match_rule_unref (rule);
+ }
+
+ /* Almost a duplicate */
+ rule = check_parse (FALSE, "path='/foo',path_namespace='/foo'");
+ _dbus_assert (rule == NULL);
+
+ /* Trailing / was supported in the initial proposal, but now isn't */
+ rule = check_parse (FALSE, "path_namespace='/foo/'");
+ _dbus_assert (rule == NULL);
+
/* Duplicates with the argN code */
rule = check_parse (FALSE, "arg0='foo',arg0='bar'");
_dbus_assert (rule == NULL);
{ "type='method_call',arg0='blah',arg1='baz'", "arg0='blah',arg1='baz',type='method_call'" },
{ "type='method_call',arg3='foosh'", "arg3='foosh',type='method_call'" },
{ "arg3='fool'", "arg3='fool'" },
+ { "arg0namespace='fool'", "arg0namespace='fool'" },
{ "member='food'", "member='food'" }
};
"type='signal',member='Frobated',arg0='foobar'",
"member='Frobated',arg0='foobar'",
"type='signal',arg0='foobar'",
+ /* The definition of argXpath matches says: "As with normal argument matches,
+ * if the argument is exactly equal to the string given in the match rule
+ * then the rule is satisfied." So this should match (even though the
+ * argument is not a valid path)!
+ */
+ "arg0path='foobar'",
+ "arg0namespace='foobar'",
NULL
};
"arg0='foobar',arg1='abcdef'",
"arg0='foobar',arg1='abcdef',arg2='abcdefghi',arg3='abcdefghi',arg4='abcdefghi'",
"arg0='foobar',arg1='abcdef',arg4='abcdefghi',arg3='abcdefghi',arg2='abcdefghi'",
+ "arg0path='foo'",
+ "arg0path='foobar/'",
+ "arg1path='3'",
+ "arg0namespace='foo'",
+ "arg0namespace='foo',arg1='abcdef'",
+ "arg0namespace='moo'",
+ NULL
+};
+
+#define EXAMPLE_NAME "com.example.backend.foo"
+
+static const char *
+should_match_message_2[] = {
+ /* EXAMPLE_NAME is in all of these namespaces */
+ "arg0namespace='com.example.backend'",
+ "arg0namespace='com.example'",
+ "arg0namespace='com'",
+
+ /* If the client specifies the name exactly, with no trailing period, then
+ * it should match.
+ */
+ "arg0namespace='com.example.backend.foo'",
+
+ NULL
+};
+
+static const char *
+should_not_match_message_2[] = {
+ /* These are not even prefixes */
+ "arg0namespace='com.example.backend.foo.bar'",
+ "arg0namespace='com.example.backend.foobar'",
+
+ /* These are prefixes, but they're not parent namespaces. */
+ "arg0namespace='com.example.backend.fo'",
+ "arg0namespace='com.example.backen'",
+ "arg0namespace='com.exampl'",
+ "arg0namespace='co'",
+
NULL
};
static void
test_matching (void)
{
- DBusMessage *message1;
+ DBusMessage *message1, *message2;
const char *v_STRING;
dbus_int32_t v_INT32;
should_not_match_message_1);
dbus_message_unref (message1);
+
+ message2 = dbus_message_new (DBUS_MESSAGE_TYPE_SIGNAL);
+ _dbus_assert (message2 != NULL);
+ if (!dbus_message_set_member (message2, "NameOwnerChanged"))
+ _dbus_assert_not_reached ("oom");
+
+ /* Obviously this isn't really a NameOwnerChanged signal. */
+ v_STRING = EXAMPLE_NAME;
+ if (!dbus_message_append_args (message2,
+ DBUS_TYPE_STRING, &v_STRING,
+ NULL))
+ _dbus_assert_not_reached ("oom");
+
+ check_matching (message2, 2,
+ should_match_message_2,
+ should_not_match_message_2);
+
+ dbus_message_unref (message2);
+}
+
+#define PATH_MATCH_RULE "arg0path='/aa/bb/'"
+
+/* This is a list of paths that should be matched by PATH_MATCH_RULE, taken
+ * from the specification. Notice that not all of them are actually legal D-Bus
+ * paths.
+ *
+ * The author of this test takes no responsibility for the semantics of
+ * this match rule key.
+ */
+static const char *paths_that_should_be_matched[] = {
+ "/aa/",
+ "/aa/bb/",
+ "/aa/bb/cc/",
+#define FIRST_VALID_PATH_WHICH_SHOULD_MATCH 3
+ "/",
+ "/aa/bb/cc",
+ NULL
+};
+
+/* These paths should not be matched by PATH_MATCH_RULE. */
+static const char *paths_that_should_not_be_matched[] = {
+ "/aa/b",
+ "/aa",
+ /* or even... */
+ "/aa/bb",
+ NULL
+};
+
+static void
+test_path_match (int type,
+ const char *path,
+ const char *rule_text,
+ BusMatchRule *rule,
+ dbus_bool_t should_match)
+{
+ DBusMessage *message = dbus_message_new (DBUS_MESSAGE_TYPE_SIGNAL);
+ dbus_bool_t matched;
+
+ _dbus_assert (message != NULL);
+ if (!dbus_message_set_member (message, "Foo"))
+ _dbus_assert_not_reached ("oom");
+
+ if (!dbus_message_append_args (message,
+ type, &path,
+ NULL))
+ _dbus_assert_not_reached ("oom");
+
+ matched = match_rule_matches (rule, NULL, NULL, message, 0);
+
+ if (matched != should_match)
+ {
+ _dbus_warn ("Expected rule %s to %s message "
+ "with first arg %s of type '%c', failed\n",
+ rule_text,
+ should_match ? "match" : "not match",
+ path,
+ (char) type);
+ exit (1);
+ }
+
+ dbus_message_unref (message);
+}
+
+static void
+test_path_matching (void)
+{
+ BusMatchRule *rule;
+ const char **s;
+
+ rule = check_parse (TRUE, PATH_MATCH_RULE);
+ _dbus_assert (rule != NULL);
+
+ for (s = paths_that_should_be_matched; *s != NULL; s++)
+ test_path_match (DBUS_TYPE_STRING, *s, PATH_MATCH_RULE, rule, TRUE);
+
+ for (s = paths_that_should_be_matched + FIRST_VALID_PATH_WHICH_SHOULD_MATCH;
+ *s != NULL; s++)
+ test_path_match (DBUS_TYPE_OBJECT_PATH, *s, PATH_MATCH_RULE, rule, TRUE);
+
+ for (s = paths_that_should_not_be_matched; *s != NULL; s++)
+ {
+ test_path_match (DBUS_TYPE_STRING, *s, PATH_MATCH_RULE, rule, FALSE);
+ test_path_match (DBUS_TYPE_OBJECT_PATH, *s, PATH_MATCH_RULE, rule, FALSE);
+ }
+
+ bus_match_rule_unref (rule);
+}
+
+static const char*
+path_namespace_should_match_message_1[] = {
+ "type='signal',path_namespace='/foo'",
+ "type='signal',path_namespace='/foo/TheObjectManager'",
+ NULL
+};
+
+static const char*
+path_namespace_should_not_match_message_1[] = {
+ "type='signal',path_namespace='/bar'",
+ "type='signal',path_namespace='/bar/TheObjectManager'",
+ NULL
+};
+
+static const char*
+path_namespace_should_match_message_2[] = {
+ "type='signal',path_namespace='/foo/TheObjectManager'",
+ NULL
+};
+
+static const char*
+path_namespace_should_not_match_message_2[] = {
+ NULL
+};
+
+static const char*
+path_namespace_should_match_message_3[] = {
+ NULL
+};
+
+static const char*
+path_namespace_should_not_match_message_3[] = {
+ "type='signal',path_namespace='/foo/TheObjectManager'",
+ NULL
+};
+
+static void
+test_matching_path_namespace (void)
+{
+ DBusMessage *message1;
+ DBusMessage *message2;
+ DBusMessage *message3;
+
+ message1 = dbus_message_new (DBUS_MESSAGE_TYPE_SIGNAL);
+ _dbus_assert (message1 != NULL);
+ if (!dbus_message_set_path (message1, "/foo/TheObjectManager"))
+ _dbus_assert_not_reached ("oom");
+
+ message2 = dbus_message_new (DBUS_MESSAGE_TYPE_SIGNAL);
+ _dbus_assert (message2 != NULL);
+ if (!dbus_message_set_path (message2, "/foo/TheObjectManager/child_object"))
+ _dbus_assert_not_reached ("oom");
+
+ message3 = dbus_message_new (DBUS_MESSAGE_TYPE_SIGNAL);
+ _dbus_assert (message3 != NULL);
+ if (!dbus_message_set_path (message3, "/foo/TheObjectManagerOther"))
+ _dbus_assert_not_reached ("oom");
+
+ check_matching (message1, 1,
+ path_namespace_should_match_message_1,
+ path_namespace_should_not_match_message_1);
+ check_matching (message2, 2,
+ path_namespace_should_match_message_2,
+ path_namespace_should_not_match_message_2);
+ check_matching (message3, 3,
+ path_namespace_should_match_message_3,
+ path_namespace_should_not_match_message_3);
+
+ dbus_message_unref (message3);
+ dbus_message_unref (message2);
+ dbus_message_unref (message1);
}
dbus_bool_t
_dbus_assert_not_reached ("Parsing match rules test failed");
test_equality ();
-
test_matching ();
-
+ test_path_matching ();
+ test_matching_path_namespace ();
+
return TRUE;
}
typedef enum
{
- BUS_MATCH_MESSAGE_TYPE = 1 << 0,
- BUS_MATCH_INTERFACE = 1 << 1,
- BUS_MATCH_MEMBER = 1 << 2,
- BUS_MATCH_SENDER = 1 << 3,
- BUS_MATCH_DESTINATION = 1 << 4,
- BUS_MATCH_PATH = 1 << 5,
- BUS_MATCH_ARGS = 1 << 6
+ BUS_MATCH_MESSAGE_TYPE = 1 << 0,
+ BUS_MATCH_INTERFACE = 1 << 1,
+ BUS_MATCH_MEMBER = 1 << 2,
+ BUS_MATCH_SENDER = 1 << 3,
+ BUS_MATCH_DESTINATION = 1 << 4,
+ BUS_MATCH_PATH = 1 << 5,
+ BUS_MATCH_ARGS = 1 << 6,
+ BUS_MATCH_PATH_NAMESPACE = 1 << 7,
+ BUS_MATCH_CLIENT_IS_EAVESDROPPING = 1 << 8
} BusMatchFlags;
BusMatchRule* bus_match_rule_new (DBusConnection *matches_go_to);
dbus_bool_t bus_match_rule_set_destination (BusMatchRule *rule,
const char *destination);
dbus_bool_t bus_match_rule_set_path (BusMatchRule *rule,
- const char *path);
+ const char *path,
+ dbus_bool_t is_namespace);
dbus_bool_t bus_match_rule_set_arg (BusMatchRule *rule,
int arg,
const DBusString *value,
- dbus_bool_t is_path);
+ dbus_bool_t is_path,
+ dbus_bool_t is_namespace);
+
+/* Calling this methods a client declares that it is creating a rule which
+ * needs to eavesdrop (e.g., dbus-monitor), any other created rules not
+ * setting themselves as eavesdropping won't receive any message not addressed
+ * to them, when eavedrop is enabled in the policy. On the other hand, when
+ * eavedrop is not enabled in policy, this method won't have any effect */
+void bus_match_rule_set_client_is_eavesdropping (BusMatchRule *rule,
+ dbus_bool_t is_eavesdropping);
BusMatchRule* bus_match_rule_parse (DBusConnection *matches_go_to,
const DBusString *rule_text,
--- /dev/null
+/* stats.c - statistics from the bus driver
+ *
+ * Licensed under the Academic Free License version 2.1
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <config.h>
+#include "stats.h"
+
+#include <dbus/dbus-internals.h>
+#include <dbus/dbus-connection-internal.h>
+
+#include "connection.h"
+#include "services.h"
+#include "utils.h"
+
+#ifdef DBUS_ENABLE_STATS
+
+static DBusMessage *
+new_asv_reply (DBusMessage *message,
+ DBusMessageIter *iter,
+ DBusMessageIter *arr_iter)
+{
+ DBusMessage *reply = dbus_message_new_method_return (message);
+
+ if (reply == NULL)
+ return NULL;
+
+ dbus_message_iter_init_append (reply, iter);
+
+ if (!dbus_message_iter_open_container (iter, DBUS_TYPE_ARRAY, "{sv}",
+ arr_iter))
+ {
+ dbus_message_unref (reply);
+ return NULL;
+ }
+
+ return reply;
+}
+
+static dbus_bool_t
+open_asv_entry (DBusMessageIter *arr_iter,
+ DBusMessageIter *entry_iter,
+ const char *key,
+ const char *type,
+ DBusMessageIter *var_iter)
+{
+ if (!dbus_message_iter_open_container (arr_iter, DBUS_TYPE_DICT_ENTRY,
+ NULL, entry_iter))
+ return FALSE;
+
+ if (!dbus_message_iter_append_basic (entry_iter, DBUS_TYPE_STRING, &key))
+ {
+ dbus_message_iter_abandon_container (arr_iter, entry_iter);
+ return FALSE;
+ }
+
+ if (!dbus_message_iter_open_container (entry_iter, DBUS_TYPE_VARIANT,
+ type, var_iter))
+ {
+ dbus_message_iter_abandon_container (arr_iter, entry_iter);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static dbus_bool_t
+close_asv_entry (DBusMessageIter *arr_iter,
+ DBusMessageIter *entry_iter,
+ DBusMessageIter *var_iter)
+{
+ if (!dbus_message_iter_close_container (entry_iter, var_iter))
+ {
+ dbus_message_iter_abandon_container (arr_iter, entry_iter);
+ return FALSE;
+ }
+
+ if (!dbus_message_iter_close_container (arr_iter, entry_iter))
+ return FALSE;
+
+ return TRUE;
+}
+
+static dbus_bool_t
+close_asv_reply (DBusMessageIter *iter,
+ DBusMessageIter *arr_iter)
+{
+ return dbus_message_iter_close_container (iter, arr_iter);
+}
+
+static void
+abandon_asv_entry (DBusMessageIter *arr_iter,
+ DBusMessageIter *entry_iter,
+ DBusMessageIter *var_iter)
+{
+ dbus_message_iter_abandon_container (entry_iter, var_iter);
+ dbus_message_iter_abandon_container (arr_iter, entry_iter);
+}
+
+static void
+abandon_asv_reply (DBusMessageIter *iter,
+ DBusMessageIter *arr_iter)
+{
+ dbus_message_iter_abandon_container (iter, arr_iter);
+}
+
+static dbus_bool_t
+asv_add_uint32 (DBusMessageIter *iter,
+ DBusMessageIter *arr_iter,
+ const char *key,
+ dbus_uint32_t value)
+{
+ DBusMessageIter entry_iter, var_iter;
+
+ if (!open_asv_entry (arr_iter, &entry_iter, key, DBUS_TYPE_UINT32_AS_STRING,
+ &var_iter))
+ goto oom;
+
+ if (!dbus_message_iter_append_basic (&var_iter, DBUS_TYPE_UINT32,
+ &value))
+ {
+ abandon_asv_entry (arr_iter, &entry_iter, &var_iter);
+ goto oom;
+ }
+
+ if (!close_asv_entry (arr_iter, &entry_iter, &var_iter))
+ goto oom;
+
+ return TRUE;
+
+oom:
+ abandon_asv_reply (iter, arr_iter);
+ return FALSE;
+}
+
+static dbus_bool_t
+asv_add_string (DBusMessageIter *iter,
+ DBusMessageIter *arr_iter,
+ const char *key,
+ const char *value)
+{
+ DBusMessageIter entry_iter, var_iter;
+
+ if (!open_asv_entry (arr_iter, &entry_iter, key, DBUS_TYPE_STRING_AS_STRING,
+ &var_iter))
+ goto oom;
+
+ if (!dbus_message_iter_append_basic (&var_iter, DBUS_TYPE_STRING,
+ &value))
+ {
+ abandon_asv_entry (arr_iter, &entry_iter, &var_iter);
+ goto oom;
+ }
+
+ if (!close_asv_entry (arr_iter, &entry_iter, &var_iter))
+ goto oom;
+
+ return TRUE;
+
+oom:
+ abandon_asv_reply (iter, arr_iter);
+ return FALSE;
+}
+
+dbus_bool_t
+bus_stats_handle_get_stats (DBusConnection *connection,
+ BusTransaction *transaction,
+ DBusMessage *message,
+ DBusError *error)
+{
+ BusConnections *connections;
+ DBusMessage *reply = NULL;
+ DBusMessageIter iter, arr_iter;
+ static dbus_uint32_t stats_serial = 0;
+ dbus_uint32_t in_use, in_free_list, allocated;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ connections = bus_transaction_get_connections (transaction);
+
+ reply = new_asv_reply (message, &iter, &arr_iter);
+
+ if (reply == NULL)
+ goto oom;
+
+ /* Globals */
+
+ if (!asv_add_uint32 (&iter, &arr_iter, "Serial", stats_serial++))
+ goto oom;
+
+ _dbus_list_get_stats (&in_use, &in_free_list, &allocated);
+ if (!asv_add_uint32 (&iter, &arr_iter, "ListMemPoolUsedBytes", in_use) ||
+ !asv_add_uint32 (&iter, &arr_iter, "ListMemPoolCachedBytes",
+ in_free_list) ||
+ !asv_add_uint32 (&iter, &arr_iter, "ListMemPoolAllocatedBytes",
+ allocated))
+ goto oom;
+
+ /* Connections */
+
+ if (!asv_add_uint32 (&iter, &arr_iter, "ActiveConnections",
+ bus_connections_get_n_active (connections)) ||
+ !asv_add_uint32 (&iter, &arr_iter, "IncompleteConnections",
+ bus_connections_get_n_incomplete (connections)) ||
+ !asv_add_uint32 (&iter, &arr_iter, "MatchRules",
+ bus_connections_get_total_match_rules (connections)) ||
+ !asv_add_uint32 (&iter, &arr_iter, "PeakMatchRules",
+ bus_connections_get_peak_match_rules (connections)) ||
+ !asv_add_uint32 (&iter, &arr_iter, "PeakMatchRulesPerConnection",
+ bus_connections_get_peak_match_rules_per_conn (connections)) ||
+ !asv_add_uint32 (&iter, &arr_iter, "BusNames",
+ bus_connections_get_total_bus_names (connections)) ||
+ !asv_add_uint32 (&iter, &arr_iter, "PeakBusNames",
+ bus_connections_get_peak_bus_names (connections)) ||
+ !asv_add_uint32 (&iter, &arr_iter, "PeakBusNamesPerConnection",
+ bus_connections_get_peak_bus_names_per_conn (connections)))
+ goto oom;
+
+ /* end */
+
+ if (!close_asv_reply (&iter, &arr_iter))
+ goto oom;
+
+ if (!bus_transaction_send_from_driver (transaction, connection, reply))
+ goto oom;
+
+ dbus_message_unref (reply);
+ return TRUE;
+
+oom:
+ if (reply != NULL)
+ dbus_message_unref (reply);
+
+ BUS_SET_OOM (error);
+ return FALSE;
+}
+
+dbus_bool_t
+bus_stats_handle_get_connection_stats (DBusConnection *caller_connection,
+ BusTransaction *transaction,
+ DBusMessage *message,
+ DBusError *error)
+{
+ const char *bus_name = NULL;
+ DBusString bus_name_str;
+ DBusMessage *reply = NULL;
+ DBusMessageIter iter, arr_iter;
+ static dbus_uint32_t stats_serial = 0;
+ dbus_uint32_t in_messages, in_bytes, in_fds, in_peak_bytes, in_peak_fds;
+ dbus_uint32_t out_messages, out_bytes, out_fds, out_peak_bytes, out_peak_fds;
+ dbus_uint32_t link_cache_bytes;
+ BusRegistry *registry;
+ BusService *service;
+ DBusConnection *stats_connection;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ registry = bus_connection_get_registry (caller_connection);
+
+ if (! dbus_message_get_args (message, error,
+ DBUS_TYPE_STRING, &bus_name,
+ DBUS_TYPE_INVALID))
+ return FALSE;
+
+ _dbus_string_init_const (&bus_name_str, bus_name);
+ service = bus_registry_lookup (registry, &bus_name_str);
+
+ if (service == NULL)
+ {
+ dbus_set_error (error, DBUS_ERROR_NAME_HAS_NO_OWNER,
+ "Bus name '%s' has no owner", bus_name);
+ return FALSE;
+ }
+
+ stats_connection = bus_service_get_primary_owners_connection (service);
+ _dbus_assert (stats_connection != NULL);
+
+ reply = new_asv_reply (message, &iter, &arr_iter);
+
+ if (reply == NULL)
+ goto oom;
+
+ /* Bus daemon per-connection stats */
+
+ if (!asv_add_uint32 (&iter, &arr_iter, "Serial", stats_serial++) ||
+ !asv_add_uint32 (&iter, &arr_iter, "MatchRules",
+ bus_connection_get_n_match_rules (stats_connection)) ||
+ !asv_add_uint32 (&iter, &arr_iter, "PeakMatchRules",
+ bus_connection_get_peak_match_rules (stats_connection)) ||
+ !asv_add_uint32 (&iter, &arr_iter, "BusNames",
+ bus_connection_get_n_services_owned (stats_connection)) ||
+ !asv_add_uint32 (&iter, &arr_iter, "PeakBusNames",
+ bus_connection_get_peak_bus_names (stats_connection)) ||
+ !asv_add_string (&iter, &arr_iter, "UniqueName",
+ bus_connection_get_name (stats_connection)))
+ goto oom;
+
+ /* DBusConnection per-connection stats */
+
+ _dbus_connection_get_stats (stats_connection,
+ &in_messages, &in_bytes, &in_fds,
+ &in_peak_bytes, &in_peak_fds,
+ &out_messages, &out_bytes, &out_fds,
+ &out_peak_bytes, &out_peak_fds,
+ &link_cache_bytes);
+
+ if (!asv_add_uint32 (&iter, &arr_iter, "IncomingMessages", in_messages) ||
+ !asv_add_uint32 (&iter, &arr_iter, "IncomingBytes", in_bytes) ||
+ !asv_add_uint32 (&iter, &arr_iter, "IncomingFDs", in_fds) ||
+ !asv_add_uint32 (&iter, &arr_iter, "PeakIncomingBytes", in_peak_bytes) ||
+ !asv_add_uint32 (&iter, &arr_iter, "PeakIncomingFDs", in_peak_fds) ||
+ !asv_add_uint32 (&iter, &arr_iter, "OutgoingMessages", out_messages) ||
+ !asv_add_uint32 (&iter, &arr_iter, "OutgoingBytes", out_bytes) ||
+ !asv_add_uint32 (&iter, &arr_iter, "OutgoingFDs", out_fds) ||
+ !asv_add_uint32 (&iter, &arr_iter, "PeakOutgoingBytes", out_peak_bytes) ||
+ !asv_add_uint32 (&iter, &arr_iter, "PeakOutgoingFDs", out_peak_fds) ||
+ !asv_add_uint32 (&iter, &arr_iter, "LinkCacheBytes", link_cache_bytes))
+ goto oom;
+
+ /* end */
+
+ if (!close_asv_reply (&iter, &arr_iter))
+ goto oom;
+
+ if (!bus_transaction_send_from_driver (transaction, caller_connection,
+ reply))
+ goto oom;
+
+ dbus_message_unref (reply);
+ return TRUE;
+
+oom:
+ if (reply != NULL)
+ dbus_message_unref (reply);
+
+ BUS_SET_OOM (error);
+ return FALSE;
+}
+
+#endif
--- /dev/null
+/* stats.h - statistics from the bus driver
+ *
+ * Licensed under the Academic Free License version 2.1
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#ifndef BUS_STATS_H
+#define BUS_STATS_H
+
+#include "bus.h"
+
+#define BUS_INTERFACE_STATS "org.freedesktop.DBus.Debug.Stats"
+
+dbus_bool_t bus_stats_handle_get_stats (DBusConnection *connection,
+ BusTransaction *transaction,
+ DBusMessage *message,
+ DBusError *error);
+
+dbus_bool_t bus_stats_handle_get_connection_stats (DBusConnection *connection,
+ BusTransaction *transaction,
+ DBusMessage *message,
+ DBusError *error);
+
+#endif /* multiple-inclusion guard */
static DBusLoop *client_loop = NULL;
static dbus_bool_t
-client_watch_callback (DBusWatch *watch,
- unsigned int condition,
- void *data)
-{
- /* FIXME this can be done in dbus-mainloop.c
- * if the code in activation.c for the babysitter
- * watch handler is fixed.
- */
-
- return dbus_watch_handle (watch, condition);
-}
-
-static dbus_bool_t
add_client_watch (DBusWatch *watch,
void *data)
{
DBusConnection *connection = data;
- return _dbus_loop_add_watch (client_loop,
- watch, client_watch_callback, connection,
- NULL);
+ return _dbus_loop_add_watch (client_loop, watch);
}
static void
{
DBusConnection *connection = data;
- _dbus_loop_remove_watch (client_loop,
- watch, client_watch_callback, connection);
-}
-
-static void
-client_timeout_callback (DBusTimeout *timeout,
- void *data)
-{
- DBusConnection *connection = data;
-
- dbus_connection_ref (connection);
-
- /* can return FALSE on OOM but we just let it fire again later */
- dbus_timeout_handle (timeout);
-
- dbus_connection_unref (connection);
+ _dbus_loop_remove_watch (client_loop, watch);
}
static dbus_bool_t
{
DBusConnection *connection = data;
- return _dbus_loop_add_timeout (client_loop, timeout, client_timeout_callback, connection, NULL);
+ return _dbus_loop_add_timeout (client_loop, timeout);
}
static void
{
DBusConnection *connection = data;
- _dbus_loop_remove_timeout (client_loop, timeout, client_timeout_callback, connection);
+ _dbus_loop_remove_timeout (client_loop, timeout);
}
static DBusHandlerResult
endif ($ENV{DBUSDIR})
if (DBUS_INSTALL_DIR)
- set(CMAKE_INSTALL_PREFIX "${DBUS_INSTALL_DIR}" CACHE TYPE PATH FORCE)
+ set(CMAKE_INSTALL_PREFIX "${DBUS_INSTALL_DIR}" CACHE PATH "install prefix" FORCE)
else (DBUS_INSTALL_DIR)
set(DBUS_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}")
endif (DBUS_INSTALL_DIR)
find_package(X11)
if(NOT WIN32)
- OPTION(DBUS_ENABLE_ABSTRACT_SOCKETS "enable support for abstract sockets" ON)
+ option (DBUS_ENABLE_ABSTRACT_SOCKETS "enable support for abstract sockets" ON)
endif(NOT WIN32)
#AC_ARG_ENABLE(asserts, AS_HELP_STRING([--enable-asserts],[include assertion checks]),enable_asserts=$enableval,enable_asserts=$USE_MAINTAINER_MODE)
-OPTION(DBUS_DISABLE_ASSERTS "Disable assertion checking" OFF)
+option (DBUS_DISABLE_ASSERTS "Disable assertion checking" OFF)
+
+option (DBUS_ENABLE_STATS "enable bus daemon usage statistics" OFF)
option (DBUS_ENABLE_STATS "enable bus daemon usage statistics" OFF)
include (MacroLibrary)
if(VCS)
- set(DBUS_VERBOSE_C_S 1 CACHE TYPE STRING FORCE)
+ set(DBUS_VERBOSE_C_S 1 CACHE STRING "verbose mode" FORCE)
set(DBUS_VERBOSE_C_S 1)
endif(VCS)
# Use the highest warning level
if (WALL)
- set(WALL 1 CACHE TYPE STRING FORCE)
- set(CMAKE_CXX_WARNING_LEVEL 4 CACHE TYPE STRING FORCE)
+ set(WALL 1 CACHE STRING "all warnings" FORCE)
+ set(CMAKE_CXX_WARNING_LEVEL 4 CACHE STRING "warning level" FORCE)
if(CMAKE_CXX_FLAGS MATCHES "/W[0-4]")
STRING(REGEX REPLACE "/W[0-4]" "/W4" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /W4")
endif(CMAKE_C_FLAGS MATCHES "/W[0-4]")
else (WALL)
- set(CMAKE_CXX_WARNING_LEVEL 3 CACHE TYPE STRING FORCE)
+ set(CMAKE_CXX_WARNING_LEVEL 3 CACHE STRING "warning level" FORCE)
endif (WALL)
SET(MSVC_W_ERROR " /we4028 /we4013 /we4133 /we4047 /we4031 /we4002 /we4003 /we4114")
# TODO: take check from configure.in
#AC_ARG_ENABLE(tests, AS_HELP_STRING([--enable-tests],[enable unit test code]),enable_tests=$enableval,enable_tests=$USE_MAINTAINER_MODE)
-OPTION(DBUS_BUILD_TESTS "enable unit test code" ON)
+option (DBUS_BUILD_TESTS "enable unit test code" ON)
if(DBUS_BUILD_TESTS)
add_definitions(-DDBUS_BUILD_TESTS -DDBUS_ENABLE_EMBEDDED_TESTS)
endif(DBUS_BUILD_TESTS)
-OPTION(DBUS_USE_OUTPUT_DEBUG_STRING "enable win32 debug port for message output" OFF)
+option (DBUS_USE_OUTPUT_DEBUG_STRING "enable win32 debug port for message output" OFF)
if(DBUS_USE_OUTPUT_DEBUG_STRING)
add_definitions(-DDBUS_USE_OUTPUT_DEBUG_STRING)
endif(DBUS_USE_OUTPUT_DEBUG_STRING)
if(WIN32)
# win32 dbus service support - this support is not complete
- OPTION(DBUS_SERVICE "enable dbus service installer" OFF)
+ option (DBUS_SERVICE "enable dbus service installer" OFF)
endif(WIN32)
#AC_ARG_ENABLE(ansi, AS_HELP_STRING([--enable-ansi],[enable -ansi -pedantic gcc flags]),enable_ansi=$enableval,enable_ansi=no)
-OPTION(DBUS_ENABLE_ANSI "enable -ansi -pedantic gcc flags" OFF)
+option (DBUS_ENABLE_ANSI "enable -ansi -pedantic gcc flags" OFF)
if(DBUS_ENABLE_ANSI)
if(NOT MSVC)
add_definitions(-ansi -D_POSIX_C_SOURCE=199309L -D_BSD_SOURCE -pedantic)
endif(DBUS_ENABLE_ANSI)
#AC_ARG_ENABLE(verbose-mode, AS_HELP_STRING([--enable-verbose-mode],[support verbose debug mode]),enable_verbose_mode=$enableval,enable_verbose_mode=$USE_MAINTAINER_MODE)
-OPTION(DBUS_ENABLE_VERBOSE_MODE "support verbose debug mode" ON)
+option (DBUS_ENABLE_VERBOSE_MODE "support verbose debug mode" ON)
#AC_ARG_ENABLE(checks, AS_HELP_STRING([--enable-checks],[include sanity checks on public API]),enable_checks=$enableval,enable_checks=yes)
-OPTION(DBUS_DISABLE_CHECKS "Disable public API sanity checking" OFF)
+option (DBUS_DISABLE_CHECKS "Disable public API sanity checking" OFF)
if(NOT MSVC)
#AC_ARG_ENABLE(gcov, AS_HELP_STRING([--enable-gcov],[compile with coverage profiling instrumentation (gcc only)]),enable_gcov=$enableval,enable_gcov=no)
- OPTION(DBUS_GCOV_ENABLED "compile with coverage profiling instrumentation (gcc only)" OFF)
+ option (DBUS_GCOV_ENABLED "compile with coverage profiling instrumentation (gcc only)" OFF)
if(DBUS_GCOV_ENABLED)
add_definitions(-fprofile-arcs -ftest-coverage)
# FIXME!!!!
#AC_ARG_ENABLE(dnotify, AS_HELP_STRING([--enable-dnotify],[build with dnotify support (linux only)]),enable_dnotify=$enableval,enable_dnotify=auto)
if("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux")
- OPTION(DBUS_BUS_ENABLE_DNOTIFY_ON_LINUX "build with dnotify support (linux only)" ON) # add a check !
+ option (DBUS_BUS_ENABLE_DNOTIFY_ON_LINUX "build with dnotify support (linux only)" ON) # add a check !
endif("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux")
#AC_ARG_ENABLE(kqueue, AS_HELP_STRING([--enable-kqueue],[build with kqueue support (FreeBSD only)]),enable_kqueue=$enableval,enable_kqueue=auto)
#AC_ARG_ENABLE(console-owner-file, AS_HELP_STRING([--enable-console-owner-file],[enable console owner file]),enable_console_owner_file=$enableval,enable_console_owner_file=auto)
STRING(TOUPPER ${CMAKE_SYSTEM_NAME} sysname)
if("${sysname}" MATCHES ".*SOLARIS.*")
- OPTION(HAVE_CONSOLE_OWNER_FILE "enable console owner file (solaris only)" ON)
+ option (HAVE_CONSOLE_OWNER_FILE "enable console owner file (solaris only)" ON)
if(HAVE_CONSOLE_OWNER_FILE)
- SET(DBUS_CONSOLE_OWNER_FILE "/dev/console" CACHE STRING "Directory to check for console ownerhip")
+ set (DBUS_CONSOLE_OWNER_FILE "/dev/console" CACHE STRING "Directory to check for console ownerhip")
endif(HAVE_CONSOLE_OWNER_FILE)
endif("${sysname}" MATCHES ".*SOLARIS.*")
endif(NOT LIBXML2_FOUND AND NOT LIBEXPAT_FOUND)
if(LIBEXPAT_FOUND)
- OPTION(DBUS_USE_EXPAT "Use expat (== ON) or libxml2 (==OFF)" ON)
+ option (DBUS_USE_EXPAT "Use expat (== ON) or libxml2 (==OFF)" ON)
else(LIBEXPAT_FOUND)
- OPTION(DBUS_USE_EXPAT "Use expat (== ON) or libxml2 (==OFF)" OFF)
+ option (DBUS_USE_EXPAT "Use expat (== ON) or libxml2 (==OFF)" OFF)
endif(LIBEXPAT_FOUND)
if(DBUS_USE_EXPAT)
endif(UNAME_EXECUTABLE)
endif(CMAKE_COMPILER_IS_GNUCC AND NOT DBUS_ENABLE_ANSI)
-OPTION(DBUS_HAVE_ATOMIC_INT "Some atomic integer implementation present" ${atomic_int})
-OPTION(DBUS_USE_ATOMIC_INT_486 "Use atomic integer implementation for 486" ${atomic_int_486})
+set (DBUS_HAVE_ATOMIC_INT ${atomic_int} CACHE STRING "Some atomic integer implementation present")
+set (DBUS_USE_ATOMIC_INT_486 ${atomic_int_486} CACHE STRING "Use atomic integer implementation for 486")
if(X11_FOUND)
- OPTION(DBUS_BUILD_X11 "Build with X11 autolaunch support " ON)
+ option (DBUS_BUILD_X11 "Build with X11 autolaunch support " ON)
endif(X11_FOUND)
# test binary names
if (WIN32)
- set (DBUS_SYSTEM_BUS_DEFAULT_ADDRESS "nonce-tcp:" CACHE STRING "system bus default address" )
- set (DBUS_SESSION_BUS_DEFAULT_ADDRESS "nonce-tcp:" CACHE STRING "session bus default address" )
+ set (DBUS_SYSTEM_BUS_DEFAULT_ADDRESS "nonce-tcp:" CACHE STRING "system bus default address")
+ set (DBUS_SESSION_BUS_DEFAULT_ADDRESS "nonce-tcp:" CACHE STRING "session bus default address")
set (DBUS_SYSTEM_CONFIG_FILE "etc/dbus-1/system.conf")
set (DBUS_SESSION_CONFIG_FILE "etc/dbus-1/session.conf")
# bus-test expects a non empty string
set (DBUS_USER "Administrator")
else (WIN32)
- set (DBUS_SYSTEM_BUS_DEFAULT_ADDRESS "unix:tmpdir=" CACHE STRING "system bus default address" )
- set (DBUS_SESSION_BUS_DEFAULT_ADDRESS "unix:path=${DBUS_SESSION_SOCKET_DIR}" CACHE STRING "session bus default address" )
+ set (DBUS_SYSTEM_BUS_DEFAULT_ADDRESS "unix:tmpdir=" CACHE STRING "system bus default address")
+ set (DBUS_SESSION_BUS_DEFAULT_ADDRESS "unix:path=${DBUS_SESSION_SOCKET_DIR}" CACHE STRING "session bus default address")
set (sysconfdir "")
set (configdir ${sysconfdir}/dbus-1 )
set (DBUS_SYSTEM_CONFIG_FILE ${configdir}/system.conf)
set (DBUS_USER "root")
endif (WIN32)
-set(DBUS_DAEMON_NAME dbus-daemon CACHE STRING "The name of the dbus daemon executable")
+set (DBUS_DAEMON_NAME "dbus-daemon" CACHE STRING "The name of the dbus daemon executable")
########### create config.h ###############
set (DBUS_INTERNAL_CLIENT_DEFINITIONS "-DDBUS_STATIC_BUILD")
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config.h )
+
+if (WIN32)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/dbus-env.bat.cmake ${CMAKE_BINARY_DIR}/bin/dbus-env.bat )
install_files(/bin FILES ${CMAKE_BINARY_DIR}/bin/dbus-env.bat)
+endif()
+
add_definitions(-DHAVE_CONFIG_H=1)
########### subdirs ###############
MESSAGE(" ")
INCLUDE(modules/CPackInstallConfig.cmake)
+
+add_custom_target(help-options
+ cmake -LH
+ WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
+)
#define _dbus_verbose_C_S _dbus_verbose
#endif
+# if defined(_MSC_VER) && !defined(inline)
+#define inline __inline
+#endif
+
#endif // _DBUS_CONFIG_H
AC_PREREQ([2.63])
m4_define([dbus_major_version], [1])
-m4_define([dbus_minor_version], [4])
-m4_define([dbus_micro_version], [13])
+m4_define([dbus_minor_version], [5])
+m4_define([dbus_micro_version], [5])
m4_define([dbus_version],
[dbus_major_version.dbus_minor_version.dbus_micro_version])
AC_INIT([dbus],[dbus_version],[https://bugs.freedesktop.org/enter_bug.cgi?product=dbus],[dbus])
#
## increment if the interface has additions, changes, removals.
-LT_CURRENT=8
+LT_CURRENT=9
## increment any time the source changes; set to
## 0 if you increment CURRENT
-LT_REVISION=7
+LT_REVISION=2
## increment if any interfaces have been added; set to 0
## if any interfaces have been changed or removed. removal has
## precedence over adding, so set to 0 if both happened.
-LT_AGE=5
+LT_AGE=6
AC_SUBST(LT_CURRENT)
AC_SUBST(LT_REVISION)
AC_CHECK_SIZEOF(long long)
AC_CHECK_SIZEOF(__int64)
+AC_ARG_WITH([64-bit],
+ [AS_HELP_STRING([--without-64-bit],
+ [If you have to use this option, please report it as a bug])],
+ [],
+ [with_64_bit=yes])
+
### See what our 64 bit type is called
AC_MSG_CHECKING([64-bit integer type])
;;
esac
-if test -z "$dbusint64" ; then
+AS_IF(
+ [test "x$with_64_bit" = xno],
+ [
DBUS_INT64_TYPE="no_int64_type_detected"
DBUS_HAVE_INT64=0
DBUS_INT64_CONSTANT=
DBUS_UINT64_CONSTANT=
- AC_MSG_RESULT([none found])
-else
+ AC_MSG_RESULT([disabled via --without-64-bit])
+ ],
+ dnl else if
+ [test -z "$dbusint64"],
+ [AC_MSG_RESULT([not found])
+ AC_MSG_ERROR([Could not find a 64-bit integer type.
+
+Please report a bug here with details of your platform and compiler:
+
+ http://bugs.freedesktop.org/enter_bug.cgi?product=DBus&component=core
+
+To compile D-Bus with all 64-bit integer types removed (not recommended), use
+the option "--without-64-bit".
+
+This option is likely to be removed in future, unless you report that your
+platform needs it.])
+ ],
+ dnl else
+ [
DBUS_INT64_TYPE="$dbusint64"
DBUS_HAVE_INT64=1
DBUS_INT64_CONSTANT="$dbusint64_constant"
AC_DEFINE_UNQUOTED(DBUS_INT64_PRINTF_MODIFIER, [$dbusint64_printf_modifier], [Define to printf modifier for 64 bit integer type])
fi
AC_MSG_RESULT($DBUS_INT64_TYPE)
-fi
+ ])
AC_SUBST(DBUS_INT64_TYPE)
AC_SUBST(DBUS_INT64_CONSTANT)
#endif
])
+AC_ARG_ENABLE([stats],
+ [AS_HELP_STRING([--enable-stats],
+ [enable bus daemon usage statistics])],
+ [], [enable_stats=no])
+if test "x$enable_stats" = xyes; then
+ AC_DEFINE([DBUS_ENABLE_STATS], [1],
+ [Define to enable bus daemon usage statistics])
+fi
+
AC_CONFIG_FILES([
Doxyfile
dbus/versioninfo.rc
Building verbose mode: ${enable_verbose_mode}
Building assertions: ${enable_asserts}
Building checks: ${enable_checks}
+ Building bus stats API: ${enable_stats}
Building SELinux support: ${have_selinux}
Building inotify support: ${have_inotify}
Building dnotify support: ${have_dnotify}
echo
echo "WARNING: You have chosen to use libxml as your xml parser however this code path is not maintained by the D-Bus developers and if it breaks you get to keep the pieces. If you have selected this option in err please reconfigure with expat (e.g. --with-xml=expat)."
fi
+
+if test "x$DBUS_HAVE_INT64" = x0; then
+ AC_MSG_WARN([You have disabled 64-bit integers via --without-64-bit.
+
+ This removes parts of the standard D-Bus API and ABI (the 't' and 'x'
+ typecodes, the dbus_int64_t and dbus_uint64_t types, etc.) and should only be
+ used if your compiler lacks support for 64-bit integers. Please report a bug
+ with details of your platform and compiler.
+
+ This option is likely to be removed in future, unless the D-Bus developers
+ receive reports that it is still needed.
+ ])
+fi
Name: dbus
Description: Free desktop message bus
Version: @VERSION@
-Libs: -L${libdir} -ldbus-1 @DBUS_CLIENT_LIBS@
+Libs: -L${libdir} -ldbus-1
+Libs.private: @DBUS_CLIENT_LIBS@
Cflags: -I${includedir}/dbus-1.0 -I${libdir}/dbus-1.0/include
* If you pass #NULL for the error, this function will not
* block; the match thus won't be added until you flush the
* connection, and if there's an error adding the match
- * (only possible error is lack of resources in the bus),
- * you won't find out about it.
+ * you won't find out about it. This is generally acceptable, since the
+ * possible errors (including a lack of resources in the bus, the connection
+ * having exceeded its quota of active match rules, or the match rule being
+ * unparseable) are generally unrecoverable.
*
* If you pass non-#NULL for the error this function will
- * block until it gets a reply.
+ * block until it gets a reply. This may be useful when using match rule keys
+ * introduced in recent versions of D-Bus, like 'arg0namespace', to allow the
+ * application to fall back to less efficient match rules supported by older
+ * versions of the daemon if the running version is not new enough; or when
+ * using user-supplied rules rather than rules hard-coded at compile time.
*
* Normal API conventions would have the function return
* a boolean value indicating whether the error was set,
DBusCondVar **dispatch_cond_loc,
DBusCondVar **io_path_cond_loc);
+/* if DBUS_ENABLE_STATS */
+void _dbus_connection_get_stats (DBusConnection *connection,
+ dbus_uint32_t *in_messages,
+ dbus_uint32_t *in_bytes,
+ dbus_uint32_t *in_fds,
+ dbus_uint32_t *in_peak_bytes,
+ dbus_uint32_t *in_peak_fds,
+ dbus_uint32_t *out_messages,
+ dbus_uint32_t *out_bytes,
+ dbus_uint32_t *out_fds,
+ dbus_uint32_t *out_peak_bytes,
+ dbus_uint32_t *out_peak_fds,
+ dbus_uint32_t *link_cache_size);
+
/* This _dbus_bus_* stuff doesn't really belong here, but dbus-bus-internal.h seems
* silly for one function
*/
#include "dbus-protocol.h"
#include "dbus-dataslot.h"
#include "dbus-string.h"
+#include "dbus-signature.h"
#include "dbus-pending-call.h"
#include "dbus-object-tree.h"
#include "dbus-threads-internal.h"
{
_dbus_return_val_if_fail (connection != NULL, FALSE);
- if (!_dbus_type_is_valid(type))
+ if (!dbus_type_is_valid (type))
return FALSE;
if (type != DBUS_TYPE_UNIX_FD)
DBusPendingCall *pending = data;
connection = _dbus_pending_call_get_connection_and_lock (pending);
+ _dbus_connection_ref_unlocked (connection);
_dbus_pending_call_queue_timeout_error_unlocked (pending,
connection);
/* Unlocks, and calls out to user code */
_dbus_connection_update_dispatch_status_and_unlock (connection, status);
+ dbus_connection_unref (connection);
return TRUE;
}
DBusPendingCall *pending;
dbus_int32_t reply_serial;
DBusDispatchStatus status;
+ dbus_bool_t found_object;
_dbus_return_val_if_fail (connection != NULL, DBUS_DISPATCH_COMPLETE);
HAVE_LOCK_CHECK (connection);
result = _dbus_object_tree_dispatch_and_unlock (connection->objects,
- message);
+ message,
+ &found_object);
CONNECTION_LOCK (connection);
}
reply = dbus_message_new_error (message,
- DBUS_ERROR_UNKNOWN_METHOD,
+ found_object ? DBUS_ERROR_UNKNOWN_METHOD : DBUS_ERROR_UNKNOWN_OBJECT,
_dbus_string_get_const_data (&str));
_dbus_string_free (&str);
}
/**
- * Registers a handler for a given path in the object hierarchy.
- * The given vtable handles messages sent to exactly the given path.
+ * Registers a handler for a given path or subsection in the object
+ * hierarchy. The given vtable handles messages sent to exactly the
+ * given path or also for paths bellow that, depending on fallback
+ * parameter.
*
* @param connection the connection
+ * @param fallback whether to handle messages also for "subdirectory"
* @param path a '/' delimited string of path elements
* @param vtable the virtual table
* @param user_data data to pass to functions in the vtable
* @returns #FALSE if an error (#DBUS_ERROR_NO_MEMORY or
* #DBUS_ERROR_OBJECT_PATH_IN_USE) is reported
*/
-dbus_bool_t
-dbus_connection_try_register_object_path (DBusConnection *connection,
- const char *path,
- const DBusObjectPathVTable *vtable,
- void *user_data,
- DBusError *error)
+static dbus_bool_t
+_dbus_connection_register_object_path (DBusConnection *connection,
+ dbus_bool_t fallback,
+ const char *path,
+ const DBusObjectPathVTable *vtable,
+ void *user_data,
+ DBusError *error)
{
char **decomposed_path;
dbus_bool_t retval;
-
- _dbus_return_val_if_fail (connection != NULL, FALSE);
- _dbus_return_val_if_fail (path != NULL, FALSE);
- _dbus_return_val_if_fail (path[0] == '/', FALSE);
- _dbus_return_val_if_fail (vtable != NULL, FALSE);
if (!_dbus_decompose_path (path, strlen (path), &decomposed_path, NULL))
return FALSE;
CONNECTION_LOCK (connection);
retval = _dbus_object_tree_register (connection->objects,
- FALSE,
+ fallback,
(const char **) decomposed_path, vtable,
user_data, error);
* Registers a handler for a given path in the object hierarchy.
* The given vtable handles messages sent to exactly the given path.
*
+ * @param connection the connection
+ * @param path a '/' delimited string of path elements
+ * @param vtable the virtual table
+ * @param user_data data to pass to functions in the vtable
+ * @param error address where an error can be returned
+ * @returns #FALSE if an error (#DBUS_ERROR_NO_MEMORY or
+ * #DBUS_ERROR_OBJECT_PATH_IN_USE) is reported
+ */
+dbus_bool_t
+dbus_connection_try_register_object_path (DBusConnection *connection,
+ const char *path,
+ const DBusObjectPathVTable *vtable,
+ void *user_data,
+ DBusError *error)
+{
+ _dbus_return_val_if_fail (connection != NULL, FALSE);
+ _dbus_return_val_if_fail (path != NULL, FALSE);
+ _dbus_return_val_if_fail (path[0] == '/', FALSE);
+ _dbus_return_val_if_fail (vtable != NULL, FALSE);
+
+ return _dbus_connection_register_object_path (connection, FALSE, path, vtable, user_data, error);
+}
+
+/**
+ * Registers a handler for a given path in the object hierarchy.
+ * The given vtable handles messages sent to exactly the given path.
+ *
* It is a bug to call this function for object paths which already
* have a handler. Use dbus_connection_try_register_object_path() if this
* might be the case.
const DBusObjectPathVTable *vtable,
void *user_data)
{
- char **decomposed_path;
dbus_bool_t retval;
DBusError error = DBUS_ERROR_INIT;
_dbus_return_val_if_fail (path[0] == '/', FALSE);
_dbus_return_val_if_fail (vtable != NULL, FALSE);
- if (!_dbus_decompose_path (path, strlen (path), &decomposed_path, NULL))
- return FALSE;
-
- CONNECTION_LOCK (connection);
-
- retval = _dbus_object_tree_register (connection->objects,
- FALSE,
- (const char **) decomposed_path, vtable,
- user_data, &error);
-
- CONNECTION_UNLOCK (connection);
-
- dbus_free_string_array (decomposed_path);
+ retval = _dbus_connection_register_object_path (connection, FALSE, path, vtable, user_data, &error);
if (dbus_error_has_name (&error, DBUS_ERROR_OBJECT_PATH_IN_USE))
{
void *user_data,
DBusError *error)
{
- char **decomposed_path;
- dbus_bool_t retval;
-
_dbus_return_val_if_fail (connection != NULL, FALSE);
_dbus_return_val_if_fail (path != NULL, FALSE);
_dbus_return_val_if_fail (path[0] == '/', FALSE);
_dbus_return_val_if_fail (vtable != NULL, FALSE);
- if (!_dbus_decompose_path (path, strlen (path), &decomposed_path, NULL))
- return FALSE;
-
- CONNECTION_LOCK (connection);
-
- retval = _dbus_object_tree_register (connection->objects,
- TRUE,
- (const char **) decomposed_path, vtable,
- user_data, error);
-
- CONNECTION_UNLOCK (connection);
-
- dbus_free_string_array (decomposed_path);
-
- return retval;
+ return _dbus_connection_register_object_path (connection, TRUE, path, vtable, user_data, error);
}
/**
const DBusObjectPathVTable *vtable,
void *user_data)
{
- char **decomposed_path;
dbus_bool_t retval;
DBusError error = DBUS_ERROR_INIT;
_dbus_return_val_if_fail (path[0] == '/', FALSE);
_dbus_return_val_if_fail (vtable != NULL, FALSE);
- if (!_dbus_decompose_path (path, strlen (path), &decomposed_path, NULL))
- return FALSE;
-
- CONNECTION_LOCK (connection);
-
- retval = _dbus_object_tree_register (connection->objects,
- TRUE,
- (const char **) decomposed_path, vtable,
- user_data, &error);
-
- CONNECTION_UNLOCK (connection);
-
- dbus_free_string_array (decomposed_path);
+ retval = _dbus_connection_register_object_path (connection, TRUE, path, vtable, user_data, &error);
if (dbus_error_has_name (&error, DBUS_ERROR_OBJECT_PATH_IN_USE))
{
return res;
}
+#ifdef DBUS_ENABLE_STATS
+void
+_dbus_connection_get_stats (DBusConnection *connection,
+ dbus_uint32_t *in_messages,
+ dbus_uint32_t *in_bytes,
+ dbus_uint32_t *in_fds,
+ dbus_uint32_t *in_peak_bytes,
+ dbus_uint32_t *in_peak_fds,
+ dbus_uint32_t *out_messages,
+ dbus_uint32_t *out_bytes,
+ dbus_uint32_t *out_fds,
+ dbus_uint32_t *out_peak_bytes,
+ dbus_uint32_t *out_peak_fds,
+ dbus_uint32_t *link_cache_size)
+{
+ CONNECTION_LOCK (connection);
+
+ if (in_messages != NULL)
+ *in_messages = connection->n_incoming;
+
+ _dbus_transport_get_stats (connection->transport,
+ in_bytes, in_fds, in_peak_bytes, in_peak_fds);
+
+ if (out_messages != NULL)
+ *out_messages = connection->n_outgoing;
+
+ if (out_bytes != NULL)
+ *out_bytes = _dbus_counter_get_size_value (connection->outgoing_counter);
+
+ if (out_fds != NULL)
+ *out_fds = _dbus_counter_get_unix_fd_value (connection->outgoing_counter);
+
+ if (out_peak_bytes != NULL)
+ *out_peak_bytes = _dbus_counter_get_peak_size_value (connection->outgoing_counter);
+
+ if (out_peak_fds != NULL)
+ *out_peak_fds = _dbus_counter_get_peak_unix_fd_value (connection->outgoing_counter);
+
+ if (link_cache_size != NULL)
+ {
+ *link_cache_size = _dbus_list_get_length (&connection->link_cache) * sizeof (DBusList);
+ }
+
+ CONNECTION_UNLOCK (connection);
+}
+#endif /* DBUS_ENABLE_STATS */
+
/**
* Gets the approximate number of uni fds of all messages in the
* outgoing message queue.
#include <pthread.h>
#endif
-#ifdef _MSC_VER
-#define inline
-#endif
#ifdef DBUS_USE_OUTPUT_DEBUG_STRING
static char module_name[1024];
#endif
}
}
+#ifdef DBUS_ENABLE_STATS
+void
+_dbus_list_get_stats (dbus_uint32_t *in_use_p,
+ dbus_uint32_t *in_free_list_p,
+ dbus_uint32_t *allocated_p)
+{
+ _DBUS_LOCK (list);
+ _dbus_mem_pool_get_stats (list_pool, in_use_p, in_free_list_p, allocated_p);
+ _DBUS_UNLOCK (list);
+}
+#endif
+
/** @} */
/**
dbus_bool_t _dbus_list_length_is_one (DBusList **list);
-
-
void _dbus_list_foreach (DBusList **list,
DBusForeachFunction function,
void *data);
#define _dbus_list_get_next_link(list, link) ((link)->next == *(list) ? NULL : (link)->next)
#define _dbus_list_get_prev_link(list, link) ((link) == *(list) ? NULL : (link)->prev)
+/* if DBUS_ENABLE_STATS */
+void _dbus_list_get_stats (dbus_uint32_t *in_use_p,
+ dbus_uint32_t *in_free_list_p,
+ dbus_uint32_t *allocated_p);
+
DBUS_END_DECLS
#endif /* DBUS_LIST_H */
#ifndef DOXYGEN_SHOULD_SKIP_THIS
+#include <dbus/dbus-hash.h>
#include <dbus/dbus-list.h>
#include <dbus/dbus-sysdeps.h>
#include <dbus/dbus-watch.h>
struct DBusLoop
{
int refcount;
- DBusList *callbacks;
+ /** fd => dbus_malloc'd DBusList ** of references to DBusWatch */
+ DBusHashTable *watches;
+ DBusList *timeouts;
int callback_list_serial;
int watch_count;
int timeout_count;
DBusList *need_dispatch;
};
-typedef enum
+static short
+watch_flags_to_poll_events (unsigned int flags)
{
- CALLBACK_WATCH,
- CALLBACK_TIMEOUT
-} CallbackType;
+ short events = 0;
-typedef struct
-{
- int refcount;
- CallbackType type;
- void *data;
- DBusFreeFunction free_data_func;
-} Callback;
+ if (flags & DBUS_WATCH_READABLE)
+ events |= _DBUS_POLLIN;
+ if (flags & DBUS_WATCH_WRITABLE)
+ events |= _DBUS_POLLOUT;
-typedef struct
+ return events;
+}
+
+static unsigned int
+watch_flags_from_poll_revents (short revents)
{
- Callback callback;
- DBusWatchFunction function;
- DBusWatch *watch;
- /* last watch handle failed due to OOM */
- unsigned int last_iteration_oom : 1;
-} WatchCallback;
+ unsigned int condition = 0;
+
+ if (revents & _DBUS_POLLIN)
+ condition |= DBUS_WATCH_READABLE;
+ if (revents & _DBUS_POLLOUT)
+ condition |= DBUS_WATCH_WRITABLE;
+ if (revents & _DBUS_POLLHUP)
+ condition |= DBUS_WATCH_HANGUP;
+ if (revents & _DBUS_POLLERR)
+ condition |= DBUS_WATCH_ERROR;
+
+ return condition;
+}
typedef struct
{
- Callback callback;
+ int refcount;
DBusTimeout *timeout;
- DBusTimeoutFunction function;
unsigned long last_tv_sec;
unsigned long last_tv_usec;
} TimeoutCallback;
-#define WATCH_CALLBACK(callback) ((WatchCallback*)callback)
#define TIMEOUT_CALLBACK(callback) ((TimeoutCallback*)callback)
-static WatchCallback*
-watch_callback_new (DBusWatch *watch,
- DBusWatchFunction function,
- void *data,
- DBusFreeFunction free_data_func)
-{
- WatchCallback *cb;
-
- cb = dbus_new (WatchCallback, 1);
- if (cb == NULL)
- return NULL;
-
- cb->watch = watch;
- cb->function = function;
- cb->last_iteration_oom = FALSE;
- cb->callback.refcount = 1;
- cb->callback.type = CALLBACK_WATCH;
- cb->callback.data = data;
- cb->callback.free_data_func = free_data_func;
-
- return cb;
-}
-
static TimeoutCallback*
-timeout_callback_new (DBusTimeout *timeout,
- DBusTimeoutFunction function,
- void *data,
- DBusFreeFunction free_data_func)
+timeout_callback_new (DBusTimeout *timeout)
{
TimeoutCallback *cb;
return NULL;
cb->timeout = timeout;
- cb->function = function;
_dbus_get_current_time (&cb->last_tv_sec,
&cb->last_tv_usec);
- cb->callback.refcount = 1;
- cb->callback.type = CALLBACK_TIMEOUT;
- cb->callback.data = data;
- cb->callback.free_data_func = free_data_func;
-
+ cb->refcount = 1;
return cb;
}
-static Callback *
-callback_ref (Callback *cb)
+static TimeoutCallback *
+timeout_callback_ref (TimeoutCallback *cb)
{
_dbus_assert (cb->refcount > 0);
}
static void
-callback_unref (Callback *cb)
+timeout_callback_unref (TimeoutCallback *cb)
{
_dbus_assert (cb->refcount > 0);
if (cb->refcount == 0)
{
- if (cb->free_data_func)
- (* cb->free_data_func) (cb->data);
-
dbus_free (cb);
}
}
-static dbus_bool_t
-add_callback (DBusLoop *loop,
- Callback *cb)
+static void
+free_watch_table_entry (void *data)
{
- if (!_dbus_list_append (&loop->callbacks, cb))
- return FALSE;
+ DBusList **watches = data;
+ DBusWatch *watch;
- loop->callback_list_serial += 1;
+ /* DBusHashTable sometimes calls free_function(NULL) even if you never
+ * have NULL as a value */
+ if (watches == NULL)
+ return;
- switch (cb->type)
+ for (watch = _dbus_list_pop_first (watches);
+ watch != NULL;
+ watch = _dbus_list_pop_first (watches))
{
- case CALLBACK_WATCH:
- loop->watch_count += 1;
- break;
- case CALLBACK_TIMEOUT:
- loop->timeout_count += 1;
- break;
+ _dbus_watch_unref (watch);
}
-
- return TRUE;
-}
-static void
-remove_callback (DBusLoop *loop,
- DBusList *link)
-{
- Callback *cb = link->data;
-
- switch (cb->type)
- {
- case CALLBACK_WATCH:
- loop->watch_count -= 1;
- break;
- case CALLBACK_TIMEOUT:
- loop->timeout_count -= 1;
- break;
- }
-
- callback_unref (cb);
- _dbus_list_remove_link (&loop->callbacks, link);
- loop->callback_list_serial += 1;
+ _dbus_assert (*watches == NULL);
+ dbus_free (watches);
}
DBusLoop*
if (loop == NULL)
return NULL;
+ loop->watches = _dbus_hash_table_new (DBUS_HASH_INT, NULL,
+ free_watch_table_entry);
+
+ if (loop->watches == NULL)
+ {
+ dbus_free (loop);
+ return NULL;
+ }
+
loop->refcount = 1;
-
+
return loop;
}
dbus_connection_unref (connection);
}
-
+
+ _dbus_hash_table_unref (loop->watches);
dbus_free (loop);
}
}
+static DBusList **
+ensure_watch_table_entry (DBusLoop *loop,
+ int fd)
+{
+ DBusList **watches;
+
+ watches = _dbus_hash_table_lookup_int (loop->watches, fd);
+
+ if (watches == NULL)
+ {
+ watches = dbus_new0 (DBusList *, 1);
+
+ if (watches == NULL)
+ return watches;
+
+ if (!_dbus_hash_table_insert_int (loop->watches, fd, watches))
+ {
+ dbus_free (watches);
+ watches = NULL;
+ }
+ }
+
+ return watches;
+}
+
+static void
+cull_watches_for_invalid_fd (DBusLoop *loop,
+ int fd)
+{
+ DBusList *link;
+ DBusList *next;
+ DBusList **watches;
+
+ _dbus_warn ("invalid request, socket fd %d not open\n", fd);
+ watches = _dbus_hash_table_lookup_int (loop->watches, fd);
+
+ if (watches != NULL)
+ {
+ for (link = _dbus_list_get_first_link (watches);
+ link != NULL;
+ link = _dbus_list_get_next_link (watches, link))
+ _dbus_watch_invalidate (link->data);
+ }
+
+ _dbus_hash_table_remove_int (loop->watches, fd);
+}
+
+static void
+gc_watch_table_entry (DBusLoop *loop,
+ DBusList **watches,
+ int fd)
+{
+ /* If watches is already NULL we have nothing to do */
+ if (watches == NULL)
+ return;
+
+ /* We can't GC hash table entries if they're non-empty lists */
+ if (*watches != NULL)
+ return;
+
+ _dbus_hash_table_remove_int (loop->watches, fd);
+}
+
dbus_bool_t
-_dbus_loop_add_watch (DBusLoop *loop,
- DBusWatch *watch,
- DBusWatchFunction function,
- void *data,
- DBusFreeFunction free_data_func)
+_dbus_loop_add_watch (DBusLoop *loop,
+ DBusWatch *watch)
{
- WatchCallback *wcb;
+ int fd;
+ DBusList **watches;
- wcb = watch_callback_new (watch, function, data, free_data_func);
- if (wcb == NULL)
+ fd = dbus_watch_get_socket (watch);
+ _dbus_assert (fd != -1);
+
+ watches = ensure_watch_table_entry (loop, fd);
+
+ if (watches == NULL)
return FALSE;
- if (!add_callback (loop, (Callback*) wcb))
+ if (_dbus_list_append (watches, _dbus_watch_ref (watch)))
{
- wcb->callback.free_data_func = NULL; /* don't want to have this side effect */
- callback_unref ((Callback*) wcb);
- return FALSE;
+ loop->callback_list_serial += 1;
+ loop->watch_count += 1;
}
-
+ else
+ {
+ _dbus_watch_unref (watch);
+ gc_watch_table_entry (loop, watches, fd);
+
+ return FALSE;
+ }
+
return TRUE;
}
void
-_dbus_loop_remove_watch (DBusLoop *loop,
- DBusWatch *watch,
- DBusWatchFunction function,
- void *data)
+_dbus_loop_remove_watch (DBusLoop *loop,
+ DBusWatch *watch)
{
+ DBusList **watches;
DBusList *link;
-
- link = _dbus_list_get_first_link (&loop->callbacks);
- while (link != NULL)
- {
- DBusList *next = _dbus_list_get_next_link (&loop->callbacks, link);
- Callback *this = link->data;
+ int fd;
+
+ /* This relies on people removing watches before they invalidate them,
+ * which has been safe since fd.o #33336 was fixed. Assert about it
+ * so we don't regress. */
+ fd = dbus_watch_get_socket (watch);
+ _dbus_assert (fd != -1);
+
+ watches = _dbus_hash_table_lookup_int (loop->watches, fd);
- if (this->type == CALLBACK_WATCH &&
- WATCH_CALLBACK (this)->watch == watch &&
- this->data == data &&
- WATCH_CALLBACK (this)->function == function)
+ if (watches != NULL)
+ {
+ link = _dbus_list_get_first_link (watches);
+ while (link != NULL)
{
- remove_callback (loop, link);
-
- return;
- }
-
- link = next;
- }
+ DBusList *next = _dbus_list_get_next_link (watches, link);
+ DBusWatch *this = link->data;
+
+ if (this == watch)
+ {
+ _dbus_list_remove_link (watches, link);
+ loop->callback_list_serial += 1;
+ loop->watch_count -= 1;
+ _dbus_watch_unref (this);
+
+ /* if that was the last watch for that fd, drop the hash table
+ * entry too */
+ gc_watch_table_entry (loop, watches, fd);
+
+ return;
+ }
- _dbus_warn ("could not find watch %p function %p data %p to remove\n",
- watch, (void *)function, data);
+ link = next;
+ }
+ }
+
+ _dbus_warn ("could not find watch %p to remove\n", watch);
}
dbus_bool_t
-_dbus_loop_add_timeout (DBusLoop *loop,
- DBusTimeout *timeout,
- DBusTimeoutFunction function,
- void *data,
- DBusFreeFunction free_data_func)
+_dbus_loop_add_timeout (DBusLoop *loop,
+ DBusTimeout *timeout)
{
TimeoutCallback *tcb;
- tcb = timeout_callback_new (timeout, function, data, free_data_func);
+ tcb = timeout_callback_new (timeout);
if (tcb == NULL)
return FALSE;
- if (!add_callback (loop, (Callback*) tcb))
+ if (_dbus_list_append (&loop->timeouts, tcb))
+ {
+ loop->callback_list_serial += 1;
+ loop->timeout_count += 1;
+ }
+ else
{
- tcb->callback.free_data_func = NULL; /* don't want to have this side effect */
- callback_unref ((Callback*) tcb);
+ timeout_callback_unref (tcb);
return FALSE;
}
}
void
-_dbus_loop_remove_timeout (DBusLoop *loop,
- DBusTimeout *timeout,
- DBusTimeoutFunction function,
- void *data)
+_dbus_loop_remove_timeout (DBusLoop *loop,
+ DBusTimeout *timeout)
{
DBusList *link;
- link = _dbus_list_get_first_link (&loop->callbacks);
+ link = _dbus_list_get_first_link (&loop->timeouts);
while (link != NULL)
{
- DBusList *next = _dbus_list_get_next_link (&loop->callbacks, link);
- Callback *this = link->data;
+ DBusList *next = _dbus_list_get_next_link (&loop->timeouts, link);
+ TimeoutCallback *this = link->data;
- if (this->type == CALLBACK_TIMEOUT &&
- TIMEOUT_CALLBACK (this)->timeout == timeout &&
- this->data == data &&
- TIMEOUT_CALLBACK (this)->function == function)
+ if (this->timeout == timeout)
{
- remove_callback (loop, link);
-
+ _dbus_list_remove_link (&loop->timeouts, link);
+ loop->callback_list_serial += 1;
+ loop->timeout_count -= 1;
+ timeout_callback_unref (this);
+
return;
}
link = next;
}
- _dbus_warn ("could not find timeout %p function %p data %p to remove\n",
- timeout, (void *)function, data);
+ _dbus_warn ("could not find timeout %p to remove\n", timeout);
}
/* Convolutions from GLib, there really must be a better way
DBusPollFD *fds;
DBusPollFD stack_fds[N_STACK_DESCRIPTORS];
int n_fds;
- WatchCallback **watches_for_fds;
- WatchCallback *stack_watches_for_fds[N_STACK_DESCRIPTORS];
int i;
DBusList *link;
int n_ready;
long timeout;
dbus_bool_t oom_watch_pending;
int orig_depth;
-
+ DBusHashIter hash_iter;
+
retval = FALSE;
fds = NULL;
- watches_for_fds = NULL;
n_fds = 0;
oom_watch_pending = FALSE;
orig_depth = loop->depth;
_dbus_verbose ("Iteration block=%d depth=%d timeout_count=%d watch_count=%d\n",
block, loop->depth, loop->timeout_count, loop->watch_count);
#endif
-
- if (loop->callbacks == NULL)
+
+ if (_dbus_hash_table_get_n_entries (loop->watches) == 0 &&
+ loop->timeouts == NULL)
goto next_iteration;
if (loop->watch_count > N_STACK_DESCRIPTORS)
_dbus_wait_for_memory ();
fds = dbus_new0 (DBusPollFD, loop->watch_count);
}
-
- watches_for_fds = dbus_new (WatchCallback*, loop->watch_count);
- while (watches_for_fds == NULL)
- {
- _dbus_wait_for_memory ();
- watches_for_fds = dbus_new (WatchCallback*, loop->watch_count);
- }
}
else
{
fds = stack_fds;
- watches_for_fds = stack_watches_for_fds;
}
/* fill our array of fds and watches */
n_fds = 0;
- link = _dbus_list_get_first_link (&loop->callbacks);
- while (link != NULL)
+ _dbus_hash_iter_init (loop->watches, &hash_iter);
+
+ while (_dbus_hash_iter_next (&hash_iter))
{
- DBusList *next = _dbus_list_get_next_link (&loop->callbacks, link);
- Callback *cb = link->data;
- if (cb->type == CALLBACK_WATCH)
+ DBusList **watches;
+ unsigned int flags;
+ int fd;
+
+ fd = _dbus_hash_iter_get_int_key (&hash_iter);
+ watches = _dbus_hash_iter_get_value (&hash_iter);
+ flags = 0;
+
+ for (link = _dbus_list_get_first_link (watches);
+ link != NULL;
+ link = _dbus_list_get_next_link (watches, link))
{
- unsigned int flags;
- WatchCallback *wcb = WATCH_CALLBACK (cb);
- int fd = dbus_watch_get_socket (wcb->watch);
+ DBusWatch *watch = link->data;
- if (wcb->last_iteration_oom)
+ if (_dbus_watch_get_oom_last_time (watch))
{
/* we skip this one this time, but reenable it next time,
* and have a timeout on this iteration
*/
- wcb->last_iteration_oom = FALSE;
+ _dbus_watch_set_oom_last_time (watch, FALSE);
oom_watch_pending = TRUE;
-
+
retval = TRUE; /* return TRUE here to keep the loop going,
* since we don't know the watch is inactive
*/
fd);
#endif
}
- else if (_DBUS_UNLIKELY (fd == -1))
+ else if (dbus_watch_get_enabled (watch))
{
- _dbus_warn ("watch %p was invalidated but not removed; "
- "removing it now\n", wcb->watch);
- _dbus_loop_remove_watch (loop, wcb->watch, wcb->function,
- ((Callback *)wcb)->data);
+ flags |= dbus_watch_get_flags (watch);
}
- else if (dbus_watch_get_enabled (wcb->watch))
- {
- watches_for_fds[n_fds] = wcb;
+ }
- callback_ref (cb);
-
- flags = dbus_watch_get_flags (wcb->watch);
-
- fds[n_fds].fd = fd;
- fds[n_fds].revents = 0;
- fds[n_fds].events = 0;
- if (flags & DBUS_WATCH_READABLE)
- fds[n_fds].events |= _DBUS_POLLIN;
- if (flags & DBUS_WATCH_WRITABLE)
- fds[n_fds].events |= _DBUS_POLLOUT;
+ if (flags != 0)
+ {
+ fds[n_fds].fd = fd;
+ fds[n_fds].revents = 0;
+ fds[n_fds].events = watch_flags_to_poll_events (flags);
#if MAINLOOP_SPEW
- _dbus_verbose (" polling watch on fd %d %s\n",
- fd, watch_flags_to_string (flags));
+ _dbus_verbose (" polling watch on fd %d %s\n",
+ loop->fds[loop->n_fds].fd, watch_flags_to_string (flags));
#endif
- n_fds += 1;
- }
- else
- {
+ n_fds += 1;
+ }
+ else
+ {
#if MAINLOOP_SPEW
- _dbus_verbose (" skipping disabled watch on fd %d %s\n",
- fd,
- watch_flags_to_string (dbus_watch_get_flags (wcb->watch)));
+ _dbus_verbose (" skipping disabled watch on fd %d %s\n",
+ fd,
+ watch_flags_to_string (dbus_watch_get_flags (watch)));
#endif
- }
}
-
- link = next;
}
-
+
timeout = -1;
if (loop->timeout_count > 0)
{
unsigned long tv_usec;
_dbus_get_current_time (&tv_sec, &tv_usec);
-
- link = _dbus_list_get_first_link (&loop->callbacks);
+
+ link = _dbus_list_get_first_link (&loop->timeouts);
while (link != NULL)
{
- DBusList *next = _dbus_list_get_next_link (&loop->callbacks, link);
- Callback *cb = link->data;
+ DBusList *next = _dbus_list_get_next_link (&loop->timeouts, link);
+ TimeoutCallback *tcb = link->data;
- if (cb->type == CALLBACK_TIMEOUT &&
- dbus_timeout_get_enabled (TIMEOUT_CALLBACK (cb)->timeout))
+ if (dbus_timeout_get_enabled (tcb->timeout))
{
- TimeoutCallback *tcb = TIMEOUT_CALLBACK (cb);
int msecs_remaining;
check_timeout (tv_sec, tv_usec, tcb, &msecs_remaining);
break; /* it's not going to get shorter... */
}
#if MAINLOOP_SPEW
- else if (cb->type == CALLBACK_TIMEOUT)
+ else
{
_dbus_verbose (" skipping disabled timeout\n");
}
_dbus_get_current_time (&tv_sec, &tv_usec);
/* It'd be nice to avoid this O(n) thingy here */
- link = _dbus_list_get_first_link (&loop->callbacks);
+ link = _dbus_list_get_first_link (&loop->timeouts);
while (link != NULL)
{
- DBusList *next = _dbus_list_get_next_link (&loop->callbacks, link);
- Callback *cb = link->data;
+ DBusList *next = _dbus_list_get_next_link (&loop->timeouts, link);
+ TimeoutCallback *tcb = link->data;
if (initial_serial != loop->callback_list_serial)
goto next_iteration;
if (loop->depth != orig_depth)
goto next_iteration;
-
- if (cb->type == CALLBACK_TIMEOUT &&
- dbus_timeout_get_enabled (TIMEOUT_CALLBACK (cb)->timeout))
+
+ if (dbus_timeout_get_enabled (tcb->timeout))
{
- TimeoutCallback *tcb = TIMEOUT_CALLBACK (cb);
int msecs_remaining;
if (check_timeout (tv_sec, tv_usec,
#if MAINLOOP_SPEW
_dbus_verbose (" invoking timeout\n");
#endif
-
- (* tcb->function) (tcb->timeout,
- cb->data);
+
+ /* can theoretically return FALSE on OOM, but we just
+ * let it fire again later - in practice that's what
+ * every wrapper callback in dbus-daemon used to do */
+ dbus_timeout_handle (tcb->timeout);
retval = TRUE;
}
}
}
#if MAINLOOP_SPEW
- else if (cb->type == CALLBACK_TIMEOUT)
+ else
{
_dbus_verbose (" skipping invocation of disabled timeout\n");
}
if (n_ready > 0)
{
- i = 0;
- while (i < n_fds)
+ for (i = 0; i < n_fds; i++)
{
+ DBusList **watches;
+ DBusList *next;
+ unsigned int condition;
+
/* FIXME I think this "restart if we change the watches"
* approach could result in starving watches
* toward the end of the list.
if (loop->depth != orig_depth)
goto next_iteration;
- if (fds[i].revents != 0)
+ if (fds[i].revents == 0)
+ continue;
+
+ if (_DBUS_UNLIKELY (fds[i].revents & _DBUS_POLLNVAL))
{
- WatchCallback *wcb;
- unsigned int condition;
-
- wcb = watches_for_fds[i];
-
- condition = 0;
- if (fds[i].revents & _DBUS_POLLIN)
- condition |= DBUS_WATCH_READABLE;
- if (fds[i].revents & _DBUS_POLLOUT)
- condition |= DBUS_WATCH_WRITABLE;
- if (fds[i].revents & _DBUS_POLLHUP)
- condition |= DBUS_WATCH_HANGUP;
- if (fds[i].revents & _DBUS_POLLERR)
- condition |= DBUS_WATCH_ERROR;
-
- /* condition may still be 0 if we got some
- * weird POLLFOO thing like POLLWRBAND
- */
-
- if (condition != 0 &&
- dbus_watch_get_enabled (wcb->watch))
+ cull_watches_for_invalid_fd (loop, fds[i].fd);
+ goto next_iteration;
+ }
+
+ condition = watch_flags_from_poll_revents (fds[i].revents);
+
+ /* condition may still be 0 if we got some
+ * weird POLLFOO thing like POLLWRBAND
+ */
+ if (condition == 0)
+ continue;
+
+ watches = _dbus_hash_table_lookup_int (loop->watches, fds[i].fd);
+
+ if (watches == NULL)
+ continue;
+
+ for (link = _dbus_list_get_first_link (watches);
+ link != NULL;
+ link = next)
+ {
+ DBusWatch *watch = link->data;
+
+ next = _dbus_list_get_next_link (watches, link);
+
+ if (dbus_watch_get_enabled (watch))
{
- if (!(* wcb->function) (wcb->watch,
- condition,
- ((Callback*)wcb)->data))
- wcb->last_iteration_oom = TRUE;
+ dbus_bool_t oom;
+
+ oom = !dbus_watch_handle (watch, condition);
+
+ if (oom)
+ {
+ _dbus_watch_set_oom_last_time (watch, TRUE);
+ }
#if MAINLOOP_SPEW
- _dbus_verbose (" Invoked watch, oom = %d\n",
- wcb->last_iteration_oom);
+ _dbus_verbose (" Invoked watch, oom = %d\n", oom);
#endif
-
retval = TRUE;
- }
- if (_DBUS_UNLIKELY (fds[i].revents & _DBUS_POLLNVAL))
- {
- _dbus_warn ("invalid request, socket fd %d not open\n",
- fds[i].fd);
- _dbus_watch_invalidate (wcb->watch);
- _dbus_loop_remove_watch (loop, wcb->watch, wcb->function,
- ((Callback *)wcb)->data);
+ /* We re-check this every time, in case the callback
+ * added/removed watches, which might make our position in
+ * the linked list invalid. See the FIXME above. */
+ if (initial_serial != loop->callback_list_serial)
+ goto next_iteration;
+
+ if (loop->depth != orig_depth)
+ goto next_iteration;
}
}
-
- ++i;
}
}
if (fds && fds != stack_fds)
dbus_free (fds);
- if (watches_for_fds)
- {
- i = 0;
- while (i < n_fds)
- {
- callback_unref (&watches_for_fds[i]->callback);
- ++i;
- }
-
- if (watches_for_fds != stack_watches_for_fds)
- dbus_free (watches_for_fds);
- }
-
+
if (_dbus_loop_dispatch (loop))
retval = TRUE;
typedef dbus_bool_t (* DBusWatchFunction) (DBusWatch *watch,
unsigned int condition,
void *data);
-typedef void (* DBusTimeoutFunction) (DBusTimeout *timeout,
- void *data);
DBusLoop* _dbus_loop_new (void);
DBusLoop* _dbus_loop_ref (DBusLoop *loop);
void _dbus_loop_unref (DBusLoop *loop);
dbus_bool_t _dbus_loop_add_watch (DBusLoop *loop,
- DBusWatch *watch,
- DBusWatchFunction function,
- void *data,
- DBusFreeFunction free_data_func);
+ DBusWatch *watch);
void _dbus_loop_remove_watch (DBusLoop *loop,
- DBusWatch *watch,
- DBusWatchFunction function,
- void *data);
+ DBusWatch *watch);
dbus_bool_t _dbus_loop_add_timeout (DBusLoop *loop,
- DBusTimeout *timeout,
- DBusTimeoutFunction function,
- void *data,
- DBusFreeFunction free_data_func);
+ DBusTimeout *timeout);
void _dbus_loop_remove_timeout (DBusLoop *loop,
- DBusTimeout *timeout,
- DBusTimeoutFunction function,
- void *data);
+ DBusTimeout *timeout);
dbus_bool_t _dbus_loop_queue_dispatch (DBusLoop *loop,
DBusConnection *connection);
}
}
-
-/**
- * Return #TRUE if the typecode is a valid typecode.
- * #DBUS_TYPE_INVALID surprisingly enough is not considered valid, and
- * random unknown bytes aren't either. This function is safe with
- * untrusted data.
- *
- * @returns #TRUE if valid
- */
-dbus_bool_t
-_dbus_type_is_valid (int typecode)
-{
- switch (typecode)
- {
- case DBUS_TYPE_BYTE:
- case DBUS_TYPE_BOOLEAN:
- case DBUS_TYPE_INT16:
- case DBUS_TYPE_UINT16:
- case DBUS_TYPE_INT32:
- case DBUS_TYPE_UINT32:
- case DBUS_TYPE_INT64:
- case DBUS_TYPE_UINT64:
- case DBUS_TYPE_DOUBLE:
- case DBUS_TYPE_STRING:
- case DBUS_TYPE_OBJECT_PATH:
- case DBUS_TYPE_SIGNATURE:
- case DBUS_TYPE_ARRAY:
- case DBUS_TYPE_STRUCT:
- case DBUS_TYPE_DICT_ENTRY:
- case DBUS_TYPE_VARIANT:
- case DBUS_TYPE_UNIX_FD:
- return TRUE;
-
- default:
- return FALSE;
- }
-}
-
/**
* Returns a string describing the given type.
*
int pos,
int byte_order,
int *new_pos);
-dbus_bool_t _dbus_type_is_valid (int typecode);
int _dbus_type_get_alignment (int typecode);
dbus_bool_t _dbus_type_is_fixed (int typecode);
int _dbus_type_get_alignment (int typecode);
}
/**
+ * Returns the header's byte order.
+ *
+ * @param header the header
+ * @returns the byte order
+ */
+char
+_dbus_header_get_byte_order (const DBusHeader *header)
+{
+ _dbus_assert (_dbus_string_get_length (&header->data) > BYTE_ORDER_OFFSET);
+
+ return (char) _dbus_string_get_byte (&header->data, BYTE_ORDER_OFFSET);
+}
+
+/**
* Revalidates the fields cache
*
* @param header the header
}
_dbus_type_reader_init (&reader,
- header->byte_order,
+ _dbus_header_get_byte_order (header),
&_dbus_header_signature_str,
FIELDS_ARRAY_SIGNATURE_OFFSET,
&header->data,
_dbus_marshal_set_uint32 (&header->data,
SERIAL_OFFSET,
serial,
- header->byte_order);
+ _dbus_header_get_byte_order (header));
}
/**
{
return _dbus_marshal_read_uint32 (&header->data,
SERIAL_OFFSET,
- header->byte_order,
+ _dbus_header_get_byte_order (header),
NULL);
}
* _dbus_header_create().
*
* @param header header to re-initialize
- * @param byte_order byte order of the header
*/
void
-_dbus_header_reinit (DBusHeader *header,
- int byte_order)
+_dbus_header_reinit (DBusHeader *header)
{
_dbus_string_set_length (&header->data, 0);
- header->byte_order = byte_order;
header->padding = 0;
_dbus_header_cache_invalidate_all (header);
* @returns #FALSE if not enough memory
*/
dbus_bool_t
-_dbus_header_init (DBusHeader *header,
- int byte_order)
+_dbus_header_init (DBusHeader *header)
{
if (!_dbus_string_init_preallocated (&header->data, 32))
return FALSE;
- _dbus_header_reinit (header, byte_order);
+ _dbus_header_reinit (header);
return TRUE;
}
*/
dbus_bool_t
_dbus_header_create (DBusHeader *header,
+ int byte_order,
int message_type,
const char *destination,
const char *path,
DBusTypeWriter writer;
DBusTypeWriter array;
+ _dbus_assert (byte_order == DBUS_LITTLE_ENDIAN ||
+ byte_order == DBUS_BIG_ENDIAN);
_dbus_assert (((interface || message_type != DBUS_MESSAGE_TYPE_SIGNAL) && member) ||
(error_name) ||
!(interface || member || error_name));
if (!reserve_header_padding (header))
return FALSE;
- _dbus_type_writer_init_values_only (&writer, header->byte_order,
+ _dbus_type_writer_init_values_only (&writer, byte_order,
&_dbus_header_signature_str, 0,
&header->data,
HEADER_END_BEFORE_PADDING (header));
- v_BYTE = header->byte_order;
+ v_BYTE = byte_order;
if (!_dbus_type_writer_write_basic (&writer, DBUS_TYPE_BYTE,
&v_BYTE))
goto oom;
_dbus_assert (bad_string_code != DBUS_VALID);
len = _dbus_marshal_read_uint32 (value_str, value_pos,
- header->byte_order, NULL);
+ _dbus_header_get_byte_order (header),
+ NULL);
#if 0
_dbus_verbose ("Validating string header field; code %d if fails\n",
_dbus_type_reader_next (&reader);
_dbus_assert (v_byte == byte_order);
- header->byte_order = byte_order;
/* MESSAGE TYPE */
_dbus_assert (_dbus_type_reader_get_current_type (&reader) == DBUS_TYPE_BYTE);
_dbus_marshal_set_uint32 (&header->data,
BODY_LENGTH_OFFSET,
body_len,
- header->byte_order);
+ _dbus_header_get_byte_order (header));
}
static dbus_bool_t
retval = FALSE;
_dbus_type_reader_init (realign_root,
- header->byte_order,
+ _dbus_header_get_byte_order (header),
&_dbus_header_signature_str,
FIELDS_ARRAY_SIGNATURE_OFFSET,
&header->data,
DBusTypeWriter array;
_dbus_type_writer_init_values_only (&writer,
- header->byte_order,
+ _dbus_header_get_byte_order (header),
&_dbus_header_signature_str,
FIELDS_ARRAY_SIGNATURE_OFFSET,
&header->data,
_dbus_marshal_read_basic (&header->data,
header->fields[field].value_pos,
- type, value, header->byte_order,
+ type, value, _dbus_header_get_byte_order (header),
NULL);
return TRUE;
_dbus_header_byteswap (DBusHeader *header,
int new_order)
{
- unsigned char byte_order;
+ char byte_order;
- if (header->byte_order == new_order)
- return;
+ byte_order = _dbus_header_get_byte_order (header);
- byte_order = _dbus_string_get_byte (&header->data, BYTE_ORDER_OFFSET);
- _dbus_assert (header->byte_order == byte_order);
+ if (byte_order == new_order)
+ return;
_dbus_marshal_byteswap (&_dbus_header_signature_str,
- 0, header->byte_order,
+ 0, byte_order,
new_order,
&header->data, 0);
_dbus_string_set_byte (&header->data, BYTE_ORDER_OFFSET, new_order);
- header->byte_order = new_order;
}
/** @} */
dbus_uint32_t byte_order : 8; /**< byte order of header */
};
-dbus_bool_t _dbus_header_init (DBusHeader *header,
- int byte_order);
+dbus_bool_t _dbus_header_init (DBusHeader *header);
void _dbus_header_free (DBusHeader *header);
-void _dbus_header_reinit (DBusHeader *header,
- int byte_order);
+void _dbus_header_reinit (DBusHeader *header);
dbus_bool_t _dbus_header_create (DBusHeader *header,
+ int byte_order,
int type,
const char *destination,
const char *path,
int len);
void _dbus_header_byteswap (DBusHeader *header,
int new_order);
+char _dbus_header_get_byte_order (const DBusHeader *header);
if (last == DBUS_DICT_ENTRY_BEGIN_CHAR)
{
- if (!(_dbus_type_is_valid (*p) && dbus_type_is_basic (*p)))
+ if (!(dbus_type_is_valid (*p) && dbus_type_is_basic (*p)))
{
result = DBUS_INVALID_DICT_KEY_MUST_BE_BASIC_TYPE;
goto out;
{
int array_elem_type = _dbus_type_reader_get_element_type (reader);
- if (!_dbus_type_is_valid (array_elem_type))
+ if (!dbus_type_is_valid (array_elem_type))
{
return DBUS_INVALID_UNKNOWN_TYPECODE;
}
((c) >= 'a' && (c) <= 'z') || \
((c) == '_') || ((c) == '-'))
-/**
- * Checks that the given range of the string is a valid bus name in
- * the D-Bus protocol. This includes a length restriction, etc., see
- * the specification.
- *
- * @todo this is inconsistent with most of DBusString in that
- * it allows a start,len range that extends past the string end.
- *
- * @param str the string
- * @param start first byte index to check
- * @param len number of bytes to check
- * @returns #TRUE if the byte range exists and is a valid name
- */
-dbus_bool_t
-_dbus_validate_bus_name (const DBusString *str,
- int start,
- int len)
+static dbus_bool_t
+_dbus_validate_bus_name_full (const DBusString *str,
+ int start,
+ int len,
+ dbus_bool_t is_namespace)
{
const unsigned char *s;
const unsigned char *end;
++s;
}
- if (_DBUS_UNLIKELY (last_dot == NULL))
+ if (!is_namespace && _DBUS_UNLIKELY (last_dot == NULL))
return FALSE;
return TRUE;
}
/**
+ * Checks that the given range of the string is a valid bus name in
+ * the D-Bus protocol. This includes a length restriction, etc., see
+ * the specification.
+ *
+ * @todo this is inconsistent with most of DBusString in that
+ * it allows a start,len range that extends past the string end.
+ *
+ * @param str the string
+ * @param start first byte index to check
+ * @param len number of bytes to check
+ * @returns #TRUE if the byte range exists and is a valid name
+ */
+dbus_bool_t
+_dbus_validate_bus_name (const DBusString *str,
+ int start,
+ int len)
+{
+ return _dbus_validate_bus_name_full (str, start, len, FALSE);
+}
+
+/**
+ * Checks that the given range of the string is a prefix of a valid bus name in
+ * the D-Bus protocol. Unlike _dbus_validate_bus_name(), this accepts strings
+ * with only one period-separated component.
+ *
+ * @todo this is inconsistent with most of DBusString in that
+ * it allows a start,len range that extends past the string end.
+ *
+ * @param str the string
+ * @param start first byte index to check
+ * @param len number of bytes to check
+ * @returns #TRUE if the byte range exists and is a valid name
+ */
+dbus_bool_t
+_dbus_validate_bus_namespace (const DBusString *str,
+ int start,
+ int len)
+{
+ return _dbus_validate_bus_name_full (str, start, len, TRUE);
+}
+
+/**
* Checks that the given range of the string is a valid message type
* signature in the D-Bus protocol.
*
dbus_bool_t _dbus_validate_bus_name (const DBusString *str,
int start,
int len);
+dbus_bool_t _dbus_validate_bus_namespace (const DBusString *str,
+ int start,
+ int len);
dbus_bool_t _dbus_validate_signature (const DBusString *str,
int start,
int len);
}
}
+#ifdef DBUS_ENABLE_STATS
+void
+_dbus_mem_pool_get_stats (DBusMemPool *pool,
+ dbus_uint32_t *in_use_p,
+ dbus_uint32_t *in_free_list_p,
+ dbus_uint32_t *allocated_p)
+{
+ DBusMemBlock *block;
+ DBusFreedElement *freed;
+ dbus_uint32_t in_use = 0;
+ dbus_uint32_t in_free_list = 0;
+ dbus_uint32_t allocated = 0;
+
+ if (pool != NULL)
+ {
+ in_use = pool->element_size * pool->allocated_elements;
+
+ for (freed = pool->free_elements; freed != NULL; freed = freed->next)
+ {
+ in_free_list += pool->element_size;
+ }
+
+ for (block = pool->blocks; block != NULL; block = block->next)
+ {
+ if (block == pool->blocks)
+ allocated += pool->block_size;
+ else
+ allocated += block->used_so_far;
+ }
+ }
+
+ if (in_use_p != NULL)
+ *in_use_p = in_use;
+
+ if (in_free_list_p != NULL)
+ *in_free_list_p = in_free_list;
+
+ if (allocated_p != NULL)
+ *allocated_p = allocated;
+}
+#endif /* DBUS_ENABLE_STATS */
+
/** @} */
#ifdef DBUS_BUILD_TESTS
dbus_bool_t _dbus_mem_pool_dealloc (DBusMemPool *pool,
void *element);
+/* if DBUS_ENABLE_STATS */
+void _dbus_mem_pool_get_stats (DBusMemPool *pool,
+ dbus_uint32_t *in_use_p,
+ dbus_uint32_t *in_free_list_p,
+ dbus_uint32_t *allocated_p);
+
DBUS_END_DECLS
#endif /* DBUS_MEMPOOL_H */
#ifdef DBUS_BUILD_TESTS
#include "dbus-message-factory.h"
#include "dbus-message-private.h"
+#include "dbus-signature.h"
#include "dbus-test.h"
#include <stdio.h>
DBusMessage *message;
DBusString signature;
DBusString body;
+ char byte_order;
/* Keeping this small makes things go faster */
message = dbus_message_new_method_call ("o.z.F",
if (message == NULL)
_dbus_assert_not_reached ("oom");
+ byte_order = _dbus_header_get_byte_order (&message->header);
+
set_reply_serial (message);
if (!_dbus_string_init (&signature) || !_dbus_string_init (&body))
_dbus_assert_not_reached ("oom");
if (dbus_internal_do_not_use_generate_bodies (iter_get_sequence (iter),
- message->byte_order,
+ byte_order,
&signature, &body))
{
const char *v_SIGNATURE;
_dbus_marshal_set_uint32 (&message->header.data, BODY_LENGTH_OFFSET,
_dbus_string_get_length (&message->body),
- message->byte_order);
+ byte_order);
*message_p = message;
}
}
else if (item_seq == 8)
{
+ char byte_order;
+
message = simple_method_call ();
+ byte_order = _dbus_header_get_byte_order (&message->header);
generate_from_message (data, expected_validity, message);
_dbus_marshal_set_uint32 (data, BODY_LENGTH_OFFSET,
DBUS_MAXIMUM_MESSAGE_LENGTH / 2 + 4,
- message->byte_order);
+ byte_order);
_dbus_marshal_set_uint32 (data, FIELDS_ARRAY_LENGTH_OFFSET,
DBUS_MAXIMUM_MESSAGE_LENGTH / 2 + 4,
- message->byte_order);
+ byte_order);
*expected_validity = DBUS_INVALID_MESSAGE_TOO_LONG;
}
else if (item_seq == 9)
_dbus_assert (byte_seq < _dbus_string_get_length (data));
- if (_dbus_type_is_valid (_dbus_string_get_byte (data, byte_seq)))
+ if (dbus_type_is_valid (_dbus_string_get_byte (data, byte_seq)))
break;
else
iter_next (iter);
DBusString body; /**< Body network data. */
- char byte_order; /**< Message byte order. */
-
unsigned int locked : 1; /**< Message being sent, no modifications allowed. */
#ifndef DBUS_DISABLE_CHECKS
{
const DBusString *type_str;
int type_pos;
-
- if (message->byte_order == DBUS_COMPILER_BYTE_ORDER)
+ char byte_order;
+
+ byte_order = _dbus_header_get_byte_order (&message->header);
+
+ if (byte_order == DBUS_COMPILER_BYTE_ORDER)
return;
_dbus_verbose ("Swapping message into compiler byte order\n");
get_const_signature (&message->header, &type_str, &type_pos);
_dbus_marshal_byteswap (type_str, type_pos,
- message->byte_order,
+ byte_order,
DBUS_COMPILER_BYTE_ORDER,
&message->body, 0);
- message->byte_order = DBUS_COMPILER_BYTE_ORDER;
-
_dbus_header_byteswap (&message->header, DBUS_COMPILER_BYTE_ORDER);
+ _dbus_assert (_dbus_header_get_byte_order (&message->header) ==
+ DBUS_COMPILER_BYTE_ORDER);
}
/** byte-swap the message if it doesn't match our byte order.
* Otherwise should not be called since it would do needless
* work.
*/
-#define ensure_byte_order(message) \
- if (message->byte_order != DBUS_COMPILER_BYTE_ORDER) \
- _dbus_message_byteswap (message)
+#define ensure_byte_order(message) _dbus_message_byteswap (message)
/**
* Gets the data to be sent over the network for this message.
static dbus_bool_t
_dbus_message_iter_check (DBusMessageRealIter *iter)
{
+ char byte_order;
+
if (iter == NULL)
{
_dbus_warn_check_failed ("dbus message iterator is NULL\n");
return FALSE;
}
+ byte_order = _dbus_header_get_byte_order (&iter->message->header);
+
if (iter->iter_type == DBUS_MESSAGE_ITER_TYPE_READER)
{
- if (iter->u.reader.byte_order != iter->message->byte_order)
+ if (iter->u.reader.byte_order != byte_order)
{
_dbus_warn_check_failed ("dbus message changed byte order since iterator was created\n");
return FALSE;
}
else if (iter->iter_type == DBUS_MESSAGE_ITER_TYPE_WRITER)
{
- if (iter->u.writer.byte_order != iter->message->byte_order)
+ if (iter->u.writer.byte_order != byte_order)
{
_dbus_warn_check_failed ("dbus message changed byte order since append iterator was created\n");
return FALSE;
}
message->refcount.value = 1;
- message->byte_order = DBUS_COMPILER_BYTE_ORDER;
message->locked = FALSE;
#ifndef DBUS_DISABLE_CHECKS
message->in_cache = FALSE;
if (from_cache)
{
- _dbus_header_reinit (&message->header, message->byte_order);
+ _dbus_header_reinit (&message->header);
_dbus_string_set_length (&message->body, 0);
}
else
{
- if (!_dbus_header_init (&message->header, message->byte_order))
+ if (!_dbus_header_init (&message->header))
{
dbus_free (message);
return NULL;
return NULL;
if (!_dbus_header_create (&message->header,
+ DBUS_COMPILER_BYTE_ORDER,
message_type,
NULL, NULL, NULL, NULL, NULL))
{
return NULL;
if (!_dbus_header_create (&message->header,
+ DBUS_COMPILER_BYTE_ORDER,
DBUS_MESSAGE_TYPE_METHOD_CALL,
destination, path, interface, method, NULL))
{
return NULL;
if (!_dbus_header_create (&message->header,
+ DBUS_COMPILER_BYTE_ORDER,
DBUS_MESSAGE_TYPE_METHOD_RETURN,
sender, NULL, NULL, NULL, NULL))
{
return NULL;
if (!_dbus_header_create (&message->header,
+ DBUS_COMPILER_BYTE_ORDER,
DBUS_MESSAGE_TYPE_SIGNAL,
NULL, path, interface, name, NULL))
{
return NULL;
if (!_dbus_header_create (&message->header,
+ DBUS_COMPILER_BYTE_ORDER,
DBUS_MESSAGE_TYPE_ERROR,
sender, NULL, NULL, NULL, error_name))
{
return NULL;
retval->refcount.value = 1;
- retval->byte_order = message->byte_order;
retval->locked = FALSE;
#ifndef DBUS_DISABLE_CHECKS
retval->generation = message->generation;
DBUS_MESSAGE_ITER_TYPE_READER);
_dbus_type_reader_init (&real->u.reader,
- message->byte_order,
+ _dbus_header_get_byte_order (&message->header),
type_str, type_pos,
&message->body,
0);
* due to OOM.
*/
_dbus_type_writer_init_types_delayed (&real->u.writer,
- message->byte_order,
+ _dbus_header_get_byte_order (&message->header),
&message->body,
_dbus_string_get_length (&message->body));
}
_dbus_assert (validity == DBUS_VALID);
- message->byte_order = byte_order;
-
/* 2. VALIDATE BODY */
if (mode != DBUS_VALIDATION_MODE_WE_TRUST_THIS_DATA_ABSOLUTELY)
{
*/
DBusHandlerResult
_dbus_object_tree_dispatch_and_unlock (DBusObjectTree *tree,
- DBusMessage *message)
+ DBusMessage *message,
+ dbus_bool_t *found_object)
{
char **path;
dbus_bool_t exact_match;
/* Find the deepest path that covers the path in the message */
subtree = find_handler (tree, (const char**) path, &exact_match);
+ if (found_object)
+ *found_object = !!subtree;
+
/* Build a list of all paths that cover the path in the message */
list = NULL;
++j;
}
- result = _dbus_object_tree_dispatch_and_unlock (tree, message);
+ result = _dbus_object_tree_dispatch_and_unlock (tree, message, NULL);
if (result == DBUS_HANDLER_RESULT_NEED_MEMORY)
goto oom;
void _dbus_object_tree_unregister_and_unlock (DBusObjectTree *tree,
const char **path);
DBusHandlerResult _dbus_object_tree_dispatch_and_unlock (DBusObjectTree *tree,
- DBusMessage *message);
+ DBusMessage *message,
+ dbus_bool_t *found_object);
void* _dbus_object_tree_get_user_data_unlocked (DBusObjectTree *tree,
const char **path);
void _dbus_object_tree_free_all_unlocked (DBusObjectTree *tree);
long size_value; /**< current size counter value */
long unix_fd_value; /**< current unix fd counter value */
+#ifdef DBUS_ENABLE_STATS
+ long peak_size_value; /**< largest ever size counter value */
+ long peak_unix_fd_value; /**< largest ever unix fd counter value */
+#endif
+
long notify_size_guard_value; /**< call notify function when crossing this size value */
long notify_unix_fd_guard_value; /**< call notify function when crossing this unix fd value */
counter->size_value = 0;
counter->unix_fd_value = 0;
+#ifdef DBUS_ENABLE_STATS
+ counter->peak_size_value = 0;
+ counter->peak_unix_fd_value = 0;
+#endif
+
counter->notify_size_guard_value = 0;
counter->notify_unix_fd_guard_value = 0;
counter->notify_function = NULL;
counter->size_value += delta;
+#ifdef DBUS_ENABLE_STATS
+ if (counter->peak_size_value < counter->size_value)
+ counter->peak_size_value = counter->size_value;
+#endif
+
#if 0
_dbus_verbose ("Adjusting counter %ld by %ld = %ld\n",
old, delta, counter->size_value);
counter->unix_fd_value += delta;
+#ifdef DBUS_ENABLE_STATS
+ if (counter->peak_unix_fd_value < counter->unix_fd_value)
+ counter->peak_unix_fd_value = counter->unix_fd_value;
+#endif
+
#if 0
_dbus_verbose ("Adjusting counter %ld by %ld = %ld\n",
old, delta, counter->unix_fd_value);
counter->notify_data = user_data;
}
+#ifdef DBUS_ENABLE_STATS
+long
+_dbus_counter_get_peak_size_value (DBusCounter *counter)
+{
+ return counter->peak_size_value;
+}
+
+long
+_dbus_counter_get_peak_unix_fd_value (DBusCounter *counter)
+{
+ return counter->peak_unix_fd_value;
+}
+#endif
+
/** @} */ /* end of resource limits exported API */
DBusCounterNotifyFunction function,
void *user_data);
+/* if DBUS_ENABLE_STATS */
+long _dbus_counter_get_peak_size_value (DBusCounter *counter);
+long _dbus_counter_get_peak_unix_fd_value (DBusCounter *counter);
DBUS_END_DECLS
{
_dbus_server_remove_watch (server,
socket_server->watch[i]);
+ _dbus_watch_invalidate (socket_server->watch[i]);
_dbus_watch_unref (socket_server->watch[i]);
socket_server->watch[i] = NULL;
}
* container types. #DBUS_TYPE_INVALID is not a container type.
*
* It is an error to pass an invalid type-code, other than DBUS_TYPE_INVALID,
- * to this function. The valid type-codes are defined by dbus-protocol.h.
+ * to this function. The valid type-codes are defined by dbus-protocol.h
+ * and can be checked with dbus_type_is_valid().
*
* @param typecode either a valid type-code or DBUS_TYPE_INVALID
* @returns #TRUE if type is a container
dbus_type_is_container (int typecode)
{
/* only reasonable (non-line-noise) typecodes are allowed */
- _dbus_return_val_if_fail (_dbus_type_is_valid (typecode) || typecode == DBUS_TYPE_INVALID,
+ _dbus_return_val_if_fail (dbus_type_is_valid (typecode) || typecode == DBUS_TYPE_INVALID,
FALSE);
return TYPE_IS_CONTAINER (typecode);
}
* type.
*
* It is an error to pass an invalid type-code, other than DBUS_TYPE_INVALID,
- * to this function. The valid type-codes are defined by dbus-protocol.h.
+ * to this function. The valid type-codes are defined by dbus-protocol.h
+ * and can be checked with dbus_type_is_valid().
*
* @param typecode either a valid type-code or DBUS_TYPE_INVALID
* @returns #TRUE if type is basic
dbus_type_is_basic (int typecode)
{
/* only reasonable (non-line-noise) typecodes are allowed */
- _dbus_return_val_if_fail (_dbus_type_is_valid (typecode) || typecode == DBUS_TYPE_INVALID,
+ _dbus_return_val_if_fail (dbus_type_is_valid (typecode) || typecode == DBUS_TYPE_INVALID,
FALSE);
/* everything that isn't invalid or a container */
* function.
*
* It is an error to pass an invalid type-code, other than DBUS_TYPE_INVALID,
- * to this function. The valid type-codes are defined by dbus-protocol.h.
+ * to this function. The valid type-codes are defined by dbus-protocol.h
+ * and can be checked with dbus_type_is_valid().
*
* @param typecode either a valid type-code or DBUS_TYPE_INVALID
* @returns #FALSE if the type can occupy different lengths
dbus_type_is_fixed (int typecode)
{
/* only reasonable (non-line-noise) typecodes are allowed */
- _dbus_return_val_if_fail (_dbus_type_is_valid (typecode) || typecode == DBUS_TYPE_INVALID,
+ _dbus_return_val_if_fail (dbus_type_is_valid (typecode) || typecode == DBUS_TYPE_INVALID,
FALSE);
switch (typecode)
}
}
+/**
+ * Return #TRUE if the argument is a valid typecode.
+ * #DBUS_TYPE_INVALID surprisingly enough is not considered valid, and
+ * random unknown bytes aren't either. This function is safe with
+ * untrusted data.
+ *
+ * @param typecode a potential type-code
+ * @returns #TRUE if valid
+ */
+dbus_bool_t
+dbus_type_is_valid (int typecode)
+{
+ switch (typecode)
+ {
+ case DBUS_TYPE_BYTE:
+ case DBUS_TYPE_BOOLEAN:
+ case DBUS_TYPE_INT16:
+ case DBUS_TYPE_UINT16:
+ case DBUS_TYPE_INT32:
+ case DBUS_TYPE_UINT32:
+ case DBUS_TYPE_INT64:
+ case DBUS_TYPE_UINT64:
+ case DBUS_TYPE_DOUBLE:
+ case DBUS_TYPE_STRING:
+ case DBUS_TYPE_OBJECT_PATH:
+ case DBUS_TYPE_SIGNATURE:
+ case DBUS_TYPE_ARRAY:
+ case DBUS_TYPE_STRUCT:
+ case DBUS_TYPE_DICT_ENTRY:
+ case DBUS_TYPE_VARIANT:
+ case DBUS_TYPE_UNIX_FD:
+ return TRUE;
+
+ default:
+ return FALSE;
+ }
+}
+
/** @} */ /* end of DBusSignature group */
#ifdef DBUS_BUILD_TESTS
DBusError *error);
DBUS_EXPORT
+dbus_bool_t dbus_type_is_valid (int typecode);
+
+DBUS_EXPORT
dbus_bool_t dbus_type_is_basic (int typecode);
DBUS_EXPORT
dbus_bool_t dbus_type_is_container (int typecode);
DBusWatchList *watches;
DBusWatch *sitter_watch;
+ DBusBabysitterFinishedFunc finished_cb;
+ void *finished_data;
dbus_bool_t have_spawn_errno;
int spawn_errno;
return sitter;
}
+static void
+close_socket_to_babysitter (DBusBabysitter *sitter)
+{
+ _dbus_verbose ("Closing babysitter\n");
+
+ if (sitter->sitter_watch != NULL)
+ {
+ _dbus_assert (sitter->watches != NULL);
+ _dbus_watch_list_remove_watch (sitter->watches, sitter->sitter_watch);
+ _dbus_watch_invalidate (sitter->sitter_watch);
+ _dbus_watch_unref (sitter->sitter_watch);
+ sitter->sitter_watch = NULL;
+ }
+
+ if (sitter->socket_to_babysitter != -1)
+ {
+ _dbus_close_socket (sitter->socket_to_babysitter, NULL);
+ sitter->socket_to_babysitter = -1;
+ }
+}
+
/**
* Decrement the reference count on the babysitter object.
*
if (sitter->refcount == 0)
{
- if (sitter->socket_to_babysitter != -1)
- {
- _dbus_close_socket (sitter->socket_to_babysitter, NULL);
- sitter->socket_to_babysitter = -1;
- }
+ close_socket_to_babysitter (sitter);
if (sitter->socket_to_main != -1)
{
*/
PING();
- _dbus_close_socket (sitter->socket_to_babysitter, NULL);
+ close_socket_to_babysitter (sitter);
PING();
- sitter->socket_to_babysitter = -1;
+
+ if (_dbus_babysitter_get_child_exited (sitter) &&
+ sitter->finished_cb != NULL)
+ {
+ sitter->finished_cb (sitter, sitter->finished_data);
+ sitter->finished_cb = NULL;
+ }
return TRUE;
}
PING();
if (!_dbus_watch_list_add_watch (sitter->watches, sitter->sitter_watch))
{
+ /* we need to free it early so the destructor won't try to remove it
+ * without it having been added, which DBusLoop doesn't allow */
+ _dbus_watch_invalidate (sitter->sitter_watch);
+ _dbus_watch_unref (sitter->sitter_watch);
+ sitter->sitter_watch = NULL;
+
_DBUS_SET_OOM (error);
goto out0;
}
return FALSE;
}
+void
+_dbus_babysitter_set_result_function (DBusBabysitter *sitter,
+ DBusBabysitterFinishedFunc finished,
+ void *user_data)
+{
+ sitter->finished_cb = finished;
+ sitter->finished_data = user_data;
+}
+
#ifdef DBUS_BUILD_TESTS
#define LIVE_CHILDREN(sitter) ((sitter)->child_handle != NULL)
DBusWatch *error_watch; /**< Error pipe watch */
DBusWatch *sitter_watch; /**< Sitter pipe watch */
+ DBusBabysitterFinishedFunc finished_cb;
+ void *finished_data;
+
int errnum; /**< Error number */
int status; /**< Exit status code */
unsigned int have_child_status : 1; /**< True if child status has been reaped */
return sitter;
}
+static void close_socket_to_babysitter (DBusBabysitter *sitter);
+static void close_error_pipe_from_child (DBusBabysitter *sitter);
+
/**
* Decrement the reference count on the babysitter object.
* When the reference count of the babysitter object reaches
sitter->refcount -= 1;
if (sitter->refcount == 0)
- {
- if (sitter->socket_to_babysitter >= 0)
- {
- /* If we haven't forked other babysitters
- * since this babysitter and socket were
- * created then this close will cause the
- * babysitter to wake up from poll with
- * a hangup and then the babysitter will
- * quit itself.
- */
- _dbus_close_socket (sitter->socket_to_babysitter, NULL);
- sitter->socket_to_babysitter = -1;
- }
+ {
+ /* If we haven't forked other babysitters
+ * since this babysitter and socket were
+ * created then this close will cause the
+ * babysitter to wake up from poll with
+ * a hangup and then the babysitter will
+ * quit itself.
+ */
+ close_socket_to_babysitter (sitter);
- if (sitter->error_pipe_from_child >= 0)
- {
- _dbus_close_socket (sitter->error_pipe_from_child, NULL);
- sitter->error_pipe_from_child = -1;
- }
+ close_error_pipe_from_child (sitter);
if (sitter->sitter_pid > 0)
{
sitter->sitter_pid = -1;
}
-
- if (sitter->error_watch)
- {
- _dbus_watch_invalidate (sitter->error_watch);
- _dbus_watch_unref (sitter->error_watch);
- sitter->error_watch = NULL;
- }
- if (sitter->sitter_watch)
- {
- _dbus_watch_invalidate (sitter->sitter_watch);
- _dbus_watch_unref (sitter->sitter_watch);
- sitter->sitter_watch = NULL;
- }
-
if (sitter->watches)
_dbus_watch_list_free (sitter->watches);
close_socket_to_babysitter (DBusBabysitter *sitter)
{
_dbus_verbose ("Closing babysitter\n");
- _dbus_close_socket (sitter->socket_to_babysitter, NULL);
- sitter->socket_to_babysitter = -1;
+
+ if (sitter->sitter_watch != NULL)
+ {
+ _dbus_assert (sitter->watches != NULL);
+ _dbus_watch_list_remove_watch (sitter->watches, sitter->sitter_watch);
+ _dbus_watch_invalidate (sitter->sitter_watch);
+ _dbus_watch_unref (sitter->sitter_watch);
+ sitter->sitter_watch = NULL;
+ }
+
+ if (sitter->socket_to_babysitter >= 0)
+ {
+ _dbus_close_socket (sitter->socket_to_babysitter, NULL);
+ sitter->socket_to_babysitter = -1;
+ }
}
static void
close_error_pipe_from_child (DBusBabysitter *sitter)
{
_dbus_verbose ("Closing child error\n");
- _dbus_close_socket (sitter->error_pipe_from_child, NULL);
- sitter->error_pipe_from_child = -1;
+
+ if (sitter->error_watch != NULL)
+ {
+ _dbus_assert (sitter->watches != NULL);
+ _dbus_watch_list_remove_watch (sitter->watches, sitter->error_watch);
+ _dbus_watch_invalidate (sitter->error_watch);
+ _dbus_watch_unref (sitter->error_watch);
+ sitter->error_watch = NULL;
+ }
+
+ if (sitter->error_pipe_from_child >= 0)
+ {
+ _dbus_close_socket (sitter->error_pipe_from_child, NULL);
+ sitter->error_pipe_from_child = -1;
+ }
}
static void
unsigned int condition,
void *data)
{
- DBusBabysitter *sitter = data;
+ DBusBabysitter *sitter = _dbus_babysitter_ref (data);
int revents;
int fd;
babysitter_iteration (sitter, FALSE))
;
- /* Those might have closed the sockets we're watching. Before returning
- * to the main loop, we must sort that out. */
+ /* fd.o #32992: if the handle_* methods closed their sockets, they previously
+ * didn't always remove the watches. Check that we don't regress. */
+ _dbus_assert (sitter->socket_to_babysitter != -1 || sitter->sitter_watch == NULL);
+ _dbus_assert (sitter->error_pipe_from_child != -1 || sitter->error_watch == NULL);
- if (sitter->error_watch != NULL && sitter->error_pipe_from_child == -1)
+ if (_dbus_babysitter_get_child_exited (sitter) &&
+ sitter->finished_cb != NULL)
{
- _dbus_watch_invalidate (sitter->error_watch);
-
- if (sitter->watches != NULL)
- _dbus_watch_list_remove_watch (sitter->watches, sitter->error_watch);
-
- _dbus_watch_unref (sitter->error_watch);
- sitter->error_watch = NULL;
- }
-
- if (sitter->sitter_watch != NULL && sitter->socket_to_babysitter == -1)
- {
- _dbus_watch_invalidate (sitter->sitter_watch);
-
- if (sitter->watches != NULL)
- _dbus_watch_list_remove_watch (sitter->watches, sitter->sitter_watch);
-
- _dbus_watch_unref (sitter->sitter_watch);
- sitter->sitter_watch = NULL;
+ sitter->finished_cb (sitter, sitter->finished_data);
+ sitter->finished_cb = NULL;
}
+ _dbus_babysitter_unref (sitter);
return TRUE;
}
if (!_dbus_watch_list_add_watch (sitter->watches, sitter->error_watch))
{
+ /* we need to free it early so the destructor won't try to remove it
+ * without it having been added, which DBusLoop doesn't allow */
+ _dbus_watch_invalidate (sitter->error_watch);
+ _dbus_watch_unref (sitter->error_watch);
+ sitter->error_watch = NULL;
+
dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
goto cleanup_and_fail;
}
if (!_dbus_watch_list_add_watch (sitter->watches, sitter->sitter_watch))
{
+ /* we need to free it early so the destructor won't try to remove it
+ * without it having been added, which DBusLoop doesn't allow */
+ _dbus_watch_invalidate (sitter->sitter_watch);
+ _dbus_watch_unref (sitter->sitter_watch);
+ sitter->sitter_watch = NULL;
+
dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
goto cleanup_and_fail;
}
return FALSE;
}
+void
+_dbus_babysitter_set_result_function (DBusBabysitter *sitter,
+ DBusBabysitterFinishedFunc finished,
+ void *user_data)
+{
+ sitter->finished_cb = finished;
+ sitter->finished_data = user_data;
+}
+
/** @} */
#ifdef DBUS_BUILD_TESTS
typedef struct DBusBabysitter DBusBabysitter;
+typedef void (* DBusBabysitterFinishedFunc) (DBusBabysitter *sitter,
+ void *user_data);
+
dbus_bool_t _dbus_spawn_async_with_babysitter (DBusBabysitter **sitter_p,
char **argv,
char **env,
DBusSpawnChildSetupFunc child_setup,
void *user_data,
DBusError *error);
+void _dbus_babysitter_set_result_function (DBusBabysitter *sitter,
+ DBusBabysitterFinishedFunc finished,
+ void *user_data);
DBusBabysitter* _dbus_babysitter_ref (DBusBabysitter *sitter);
void _dbus_babysitter_unref (DBusBabysitter *sitter);
void _dbus_babysitter_kill_child (DBusBabysitter *sitter);
unsigned char *str; /**< String data, plus nul termination */
int len; /**< Length without nul */
int allocated; /**< Allocated size of data */
- int max_length; /**< Max length of this string, without nul byte */
unsigned int constant : 1; /**< String data is not owned by DBusString */
unsigned int locked : 1; /**< DBusString has been locked and can't be changed */
unsigned int invalid : 1; /**< DBusString is invalid (e.g. already freed) */
*/
/**
- * This is the maximum max length (and thus also the maximum length)
- * of a DBusString
+ * The maximum length of a DBusString
*/
-#define _DBUS_STRING_MAX_MAX_LENGTH (_DBUS_INT32_MAX - _DBUS_STRING_ALLOCATION_PADDING)
+#define _DBUS_STRING_MAX_LENGTH (_DBUS_INT32_MAX - _DBUS_STRING_ALLOCATION_PADDING)
/**
* Checks a bunch of assertions about a string object
*
* @param real the DBusRealString
*/
-#define DBUS_GENERIC_STRING_PREAMBLE(real) _dbus_assert ((real) != NULL); _dbus_assert (!(real)->invalid); _dbus_assert ((real)->len >= 0); _dbus_assert ((real)->allocated >= 0); _dbus_assert ((real)->max_length >= 0); _dbus_assert ((real)->len <= ((real)->allocated - _DBUS_STRING_ALLOCATION_PADDING)); _dbus_assert ((real)->len <= (real)->max_length)
+#define DBUS_GENERIC_STRING_PREAMBLE(real) \
+ do { \
+ _dbus_assert ((real) != NULL); \
+ _dbus_assert (!(real)->invalid); \
+ _dbus_assert ((real)->len >= 0); \
+ _dbus_assert ((real)->allocated >= 0); \
+ _dbus_assert ((real)->len <= ((real)->allocated - _DBUS_STRING_ALLOCATION_PADDING)); \
+ _dbus_assert ((real)->len <= _DBUS_STRING_MAX_LENGTH); \
+ } while (0)
/**
* Checks assertions about a string object that needs to be
#include <stdio.h>
static void
-test_max_len (DBusString *str,
- int max_len)
-{
- if (max_len > 0)
- {
- if (!_dbus_string_set_length (str, max_len - 1))
- _dbus_assert_not_reached ("setting len to one less than max should have worked");
- }
-
- if (!_dbus_string_set_length (str, max_len))
- _dbus_assert_not_reached ("setting len to max len should have worked");
-
- if (_dbus_string_set_length (str, max_len + 1))
- _dbus_assert_not_reached ("setting len to one more than max len should not have worked");
-
- if (!_dbus_string_set_length (str, 0))
- _dbus_assert_not_reached ("setting len to zero should have worked");
-}
-
-static void
test_hex_roundtrip (const unsigned char *data,
int len)
{
}
}
-#ifdef DBUS_BUILD_TESTS
-/* The max length thing is sort of a historical artifact
- * from a feature that turned out to be dumb; perhaps
- * we should purge it entirely. The problem with
- * the feature is that it looks like memory allocation
- * failure, but is not a transient or resolvable failure.
- */
-static void
-set_max_length (DBusString *str,
- int max_length)
-{
- DBusRealString *real;
-
- real = (DBusRealString*) str;
-
- real->max_length = max_length;
-}
-#endif /* DBUS_BUILD_TESTS */
-
/**
* @ingroup DBusStringInternals
* Unit test for DBusString.
{
DBusString str;
DBusString other;
- int i, end;
+ int i, a, end;
long v;
double d;
int lens[] = { 0, 1, 2, 3, 4, 5, 10, 16, 17, 18, 25, 31, 32, 33, 34, 35, 63, 64, 65, 66, 67, 68, 69, 70, 71, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136 };
char *s;
dbus_unichar_t ch;
-
- i = 0;
- while (i < _DBUS_N_ELEMENTS (lens))
- {
- if (!_dbus_string_init (&str))
- _dbus_assert_not_reached ("failed to init string");
-
- set_max_length (&str, lens[i]);
-
- test_max_len (&str, lens[i]);
- _dbus_string_free (&str);
-
- ++i;
- }
/* Test shortening and setting length */
i = 0;
if (!_dbus_string_init (&str))
_dbus_assert_not_reached ("failed to init string");
- set_max_length (&str, lens[i]);
-
if (!_dbus_string_set_length (&str, lens[i]))
_dbus_assert_not_reached ("failed to set string length");
_dbus_assert (_dbus_string_get_length (&other) == i * 2 - 1);
_dbus_assert (_dbus_string_equal_c_str (&other,
"HelloHello WorldWorle"));
-
+
_dbus_string_free (&str);
_dbus_string_free (&other);
+
+ /* Different tests are provided because different behaviours are
+ * implemented in _dbus_string_replace_len() in function of replacing and
+ * replaced lengths
+ */
+
+ if (!_dbus_string_init (&str))
+ _dbus_assert_not_reached ("failed to init string");
+
+ if (!_dbus_string_append (&str, "Hello World"))
+ _dbus_assert_not_reached ("could not append to string");
+
+ i = _dbus_string_get_length (&str);
+ if (!_dbus_string_init (&other))
+ _dbus_assert_not_reached ("could not init string");
+
+ if (!_dbus_string_append (&other, "Foo String"))
+ _dbus_assert_not_reached ("could not append to string");
+
+ a = _dbus_string_get_length (&other);
+
+ if (!_dbus_string_replace_len (&str, 0, 6,
+ &other, 4, 0))
+ _dbus_assert_not_reached ("could not replace 0 length");
+
+ _dbus_assert (_dbus_string_get_length (&str) == i);
+ _dbus_assert (_dbus_string_get_length (&other) == a + 6);
+ _dbus_assert (_dbus_string_equal_c_str (&other,
+ "Foo Hello String"));
+
+ if (!_dbus_string_replace_len (&str, 5, 6,
+ &other,
+ _dbus_string_get_length (&other),
+ 0))
+ _dbus_assert_not_reached ("could not replace at the end");
+
+ _dbus_assert (_dbus_string_get_length (&str) == i);
+ _dbus_assert (_dbus_string_get_length (&other) == a + 6 + 6);
+ _dbus_assert (_dbus_string_equal_c_str (&other,
+ "Foo Hello String World"));
+
+ if (!_dbus_string_replace_len (&str, 0, 5,
+ &other,
+ _dbus_string_get_length (&other) - 5,
+ 5))
+ _dbus_assert_not_reached ("could not replace same length");
+
+ _dbus_assert (_dbus_string_get_length (&str) == i);
+ _dbus_assert (_dbus_string_get_length (&other) == a + 6 + 6);
+ _dbus_assert (_dbus_string_equal_c_str (&other,
+ "Foo Hello String Hello"));
+
+ if (!_dbus_string_replace_len (&str, 6, 5,
+ &other, 4, 12))
+ _dbus_assert_not_reached ("could not replace with shorter string");
+
+ _dbus_assert (_dbus_string_get_length (&str) == i);
+ _dbus_assert (_dbus_string_get_length (&other) == a + 5);
+ _dbus_assert (_dbus_string_equal_c_str (&other,
+ "Foo World Hello"));
+
+ if (!_dbus_string_replace_len (&str, 0, 1,
+ &other, 0, 3))
+ _dbus_assert_not_reached ("could not replace at the beginning");
+
+ _dbus_assert (_dbus_string_get_length (&str) == i);
+ _dbus_assert (_dbus_string_get_length (&other) == a + 3);
+ _dbus_assert (_dbus_string_equal_c_str (&other,
+ "H World Hello"));
+
+ if (!_dbus_string_replace_len (&str, 6, 5,
+ &other,
+ _dbus_string_get_length (&other) - 5,
+ 5))
+ _dbus_assert_not_reached ("could not replace same length");
+
+ _dbus_assert (_dbus_string_get_length (&str) == i);
+ _dbus_assert (_dbus_string_get_length (&other) == a + 3);
+ _dbus_assert (_dbus_string_equal_c_str (&other,
+ "H World World"));
+
+ _dbus_string_free (&str);
+ _dbus_string_free (&other);
+
/* Check append/get unichar */
if (!_dbus_string_init (&str))
real->len = 0;
real->str[real->len] = '\0';
- real->max_length = _DBUS_STRING_MAX_MAX_LENGTH;
real->constant = FALSE;
real->locked = FALSE;
real->invalid = FALSE;
return _dbus_string_init_preallocated (str, 0);
}
-#ifdef DBUS_BUILD_TESTS
-/* The max length thing is sort of a historical artifact
- * from a feature that turned out to be dumb; perhaps
- * we should purge it entirely. The problem with
- * the feature is that it looks like memory allocation
- * failure, but is not a transient or resolvable failure.
- */
-static void
-set_max_length (DBusString *str,
- int max_length)
-{
- DBusRealString *real;
-
- real = (DBusRealString*) str;
-
- real->max_length = max_length;
-}
-#endif /* DBUS_BUILD_TESTS */
-
/**
* Initializes a constant string. The value parameter is not copied
* (should be static), and the string may never be modified.
_dbus_assert (str != NULL);
_dbus_assert (len == 0 || value != NULL);
- _dbus_assert (len <= _DBUS_STRING_MAX_MAX_LENGTH);
+ _dbus_assert (len <= _DBUS_STRING_MAX_LENGTH);
_dbus_assert (len >= 0);
real = (DBusRealString*) str;
real->str = (unsigned char*) value;
real->len = len;
real->allocated = real->len + _DBUS_STRING_ALLOCATION_PADDING; /* a lie, just to avoid special-case assertions... */
- real->max_length = real->len + 1;
real->constant = TRUE;
real->locked = TRUE;
real->invalid = FALSE;
/* at least double our old allocation to avoid O(n), avoiding
* overflow
*/
- if (real->allocated > (_DBUS_STRING_MAX_MAX_LENGTH + _DBUS_STRING_ALLOCATION_PADDING) / 2)
- new_allocated = _DBUS_STRING_MAX_MAX_LENGTH + _DBUS_STRING_ALLOCATION_PADDING;
+ if (real->allocated > (_DBUS_STRING_MAX_LENGTH + _DBUS_STRING_ALLOCATION_PADDING) / 2)
+ new_allocated = _DBUS_STRING_MAX_LENGTH + _DBUS_STRING_ALLOCATION_PADDING;
else
new_allocated = real->allocated * 2;
/* Note, we are setting the length not including nul termination */
/* exceeding max length is the same as failure to allocate memory */
- if (_DBUS_UNLIKELY (new_length > real->max_length))
+ if (_DBUS_UNLIKELY (new_length > _DBUS_STRING_MAX_LENGTH))
return FALSE;
else if (new_length > (real->allocated - _DBUS_STRING_ALLOCATION_PADDING) &&
_DBUS_UNLIKELY (!reallocate_for_length (real, new_length)))
if (len == 0)
return TRUE;
- if (len > dest->max_length - dest->len)
+ if (len > _DBUS_STRING_MAX_LENGTH - dest->len)
return FALSE; /* detected overflow of dest->len + len below */
if (!set_length (dest, dest->len + len))
_dbus_string_steal_data (DBusString *str,
char **data_return)
{
- int old_max_length;
DBUS_STRING_PREAMBLE (str);
_dbus_assert (data_return != NULL);
*data_return = (char*) real->str;
- old_max_length = real->max_length;
-
/* reset the string */
if (!_dbus_string_init (str))
{
return FALSE;
}
- real->max_length = old_max_length;
-
return TRUE;
}
-#ifdef DBUS_BUILD_TESTS
-/**
- * Like _dbus_string_get_data_len(), but removes the gotten data from
- * the original string. The caller must free the data returned. This
- * function may fail due to lack of memory, and return #FALSE.
- * The returned string is nul-terminated and has length len.
- *
- * @todo this function is broken because on failure it
- * may corrupt the source string.
- *
- * @param str the string
- * @param data_return location to return the buffer
- * @param start the start of segment to steal
- * @param len the length of segment to steal
- * @returns #TRUE on success
- */
-dbus_bool_t
-_dbus_string_steal_data_len (DBusString *str,
- char **data_return,
- int start,
- int len)
-{
- DBusString dest;
- DBUS_STRING_PREAMBLE (str);
- _dbus_assert (data_return != NULL);
- _dbus_assert (start >= 0);
- _dbus_assert (len >= 0);
- _dbus_assert (start <= real->len);
- _dbus_assert (len <= real->len - start);
-
- if (!_dbus_string_init (&dest))
- return FALSE;
-
- set_max_length (&dest, real->max_length);
-
- if (!_dbus_string_move_len (str, start, len, &dest, 0))
- {
- _dbus_string_free (&dest);
- return FALSE;
- }
-
- _dbus_warn ("Broken code in _dbus_string_steal_data_len(), see @todo, FIXME\n");
- if (!_dbus_string_steal_data (&dest, data_return))
- {
- _dbus_string_free (&dest);
- return FALSE;
- }
-
- _dbus_string_free (&dest);
- return TRUE;
-}
-#endif /* DBUS_BUILD_TESTS */
-
/**
* Copies the data from the string into a char*
*
memcpy (buffer, real->str, real->len+1);
}
-#ifdef DBUS_BUILD_TESTS
-/**
- * Copies a segment of the string into a char*
- *
- * @param str the string
- * @param data_return place to return the data
- * @param start start index
- * @param len length to copy
- * @returns #FALSE if no memory
- */
-dbus_bool_t
-_dbus_string_copy_data_len (const DBusString *str,
- char **data_return,
- int start,
- int len)
-{
- DBusString dest;
-
- DBUS_CONST_STRING_PREAMBLE (str);
- _dbus_assert (data_return != NULL);
- _dbus_assert (start >= 0);
- _dbus_assert (len >= 0);
- _dbus_assert (start <= real->len);
- _dbus_assert (len <= real->len - start);
-
- if (!_dbus_string_init (&dest))
- return FALSE;
-
- set_max_length (&dest, real->max_length);
-
- if (!_dbus_string_copy_len (str, start, len, &dest, 0))
- {
- _dbus_string_free (&dest);
- return FALSE;
- }
-
- if (!_dbus_string_steal_data (&dest, data_return))
- {
- _dbus_string_free (&dest);
- return FALSE;
- }
-
- _dbus_string_free (&dest);
- return TRUE;
-}
-#endif /* DBUS_BUILD_TESTS */
-
/* Only have the function if we don't have the macro */
#ifndef _dbus_string_get_length
/**
DBUS_STRING_PREAMBLE (str);
_dbus_assert (additional_length >= 0);
- if (_DBUS_UNLIKELY (additional_length > real->max_length - real->len))
+ if (_DBUS_UNLIKELY (additional_length > _DBUS_STRING_MAX_LENGTH - real->len))
return FALSE; /* would overflow */
return set_length (real,
gap_pos = _DBUS_ALIGN_VALUE (insert_at, alignment);
new_len = real->len + (gap_pos - insert_at) + gap_size;
- if (_DBUS_UNLIKELY (new_len > (unsigned long) real->max_length))
+ if (_DBUS_UNLIKELY (new_len > (unsigned long) _DBUS_STRING_MAX_LENGTH))
return FALSE;
delta = new_len - real->len;
_dbus_assert (buffer != NULL);
buffer_len = strlen (buffer);
- if (buffer_len > (unsigned long) real->max_length)
+ if (buffer_len > (unsigned long) _DBUS_STRING_MAX_LENGTH)
return FALSE;
return append (real, buffer, buffer_len);
len = 6;
}
- if (len > (real->max_length - real->len))
+ if (len > (_DBUS_STRING_MAX_LENGTH - real->len))
return FALSE; /* real->len + len would overflow */
if (!set_length (real, real->len + len))
* Like _dbus_string_move(), but can move a segment from
* the middle of the source string.
*
- * @todo this doesn't do anything with max_length field.
- * we should probably just kill the max_length field though.
- *
* @param source the source string
* @param start first byte of source string to move
* @param len length of segment to move
/**
* Replaces a segment of dest string with a segment of source string.
*
- * @todo optimize the case where the two lengths are the same, and
- * avoid memmoving the data in the trailing part of the string twice.
- *
- * @todo avoid inserting the source into dest, then deleting
- * the replaced chunk of dest (which creates a potentially large
- * intermediate string). Instead, extend the replaced chunk
- * of dest with padding to the same size as the source chunk,
- * then copy in the source bytes.
- *
* @param source the source string
* @param start where to start copying the source string
* @param len length of segment to copy
_dbus_assert (replace_at <= real_dest->len);
_dbus_assert (replace_len <= real_dest->len - replace_at);
- if (!copy (real_source, start, len,
- real_dest, replace_at))
- return FALSE;
+ if (len == replace_len)
+ {
+ memmove (real_dest->str + replace_at,
+ real_source->str + start, len);
+ }
+ else if (len < replace_len)
+ {
+ memmove (real_dest->str + replace_at,
+ real_source->str + start, len);
+ delete (real_dest, replace_at + len,
+ replace_len - len);
+ }
+ else
+ {
+ int diff;
+
+ _dbus_assert (len > replace_len);
+
+ diff = len - replace_len;
- delete (real_dest, replace_at + len, replace_len);
+ /* First of all we check if destination string can be enlarged as
+ * required, then we overwrite previous bytes
+ */
+
+ if (!copy (real_source, start + replace_len, diff,
+ real_dest, replace_at + replace_len))
+ return FALSE;
+
+ memmove (real_dest->str + replace_at,
+ real_source->str + start, replace_len);
+ }
return TRUE;
}
#endif
int dummy2; /**< placeholder */
int dummy3; /**< placeholder */
- int dummy4; /**< placeholder */
- unsigned int dummy5 : 1; /**< placeholder */
- unsigned int dummy6 : 1; /**< placeholder */
- unsigned int dummy7 : 1; /**< placeholder */
- unsigned int dummy8 : 3; /**< placeholder */
+ unsigned int dummy_bit1 : 1; /**< placeholder */
+ unsigned int dummy_bit2 : 1; /**< placeholder */
+ unsigned int dummy_bit3 : 1; /**< placeholder */
+ unsigned int dummy_bits : 3; /**< placeholder */
};
#ifdef DBUS_DISABLE_ASSERT
sizeof(_dbus_static_string_##name), \
sizeof(_dbus_static_string_##name) + \
_DBUS_STRING_ALLOCATION_PADDING, \
- sizeof(_dbus_static_string_##name), \
TRUE, TRUE, FALSE, 0 }
DBUS_END_DECLS
}
}
-dbus_bool_t
-_dbus_open_tcp_socket (int *fd,
- DBusError *error)
-{
- return _dbus_open_socket(fd, AF_INET, SOCK_STREAM, 0, error);
-}
-
/**
* Opens a UNIX domain socket (as in the socket() call).
* Does not bind the socket.
* @param error return location for an error
* @returns #FALSE if error is set
*/
-dbus_bool_t
+static dbus_bool_t
_dbus_open_unix_socket (int *fd,
DBusError *error)
{
int start2,
int len2);
-dbus_bool_t _dbus_open_unix_socket (int *fd,
- DBusError *error);
int _dbus_connect_unix_socket (const char *path,
dbus_bool_t abstract,
DBusError *error);
*
*/
-dbus_bool_t _dbus_open_tcp_socket (int *fd,
- DBusError *error);
dbus_bool_t _dbus_close_socket (int fd,
DBusError *error);
int _dbus_read_socket (int fd,
return (DBusTransport*) socket_transport;
failed_4:
+ _dbus_watch_invalidate (socket_transport->read_watch);
_dbus_watch_unref (socket_transport->read_watch);
failed_3:
+ _dbus_watch_invalidate (socket_transport->write_watch);
_dbus_watch_unref (socket_transport->write_watch);
failed_2:
_dbus_string_free (&socket_transport->encoded_incoming);
transport->allow_anonymous = value != FALSE;
}
+#ifdef DBUS_ENABLE_STATS
+void
+_dbus_transport_get_stats (DBusTransport *transport,
+ dbus_uint32_t *queue_bytes,
+ dbus_uint32_t *queue_fds,
+ dbus_uint32_t *peak_queue_bytes,
+ dbus_uint32_t *peak_queue_fds)
+{
+ if (queue_bytes != NULL)
+ *queue_bytes = _dbus_counter_get_size_value (transport->live_messages);
+
+ if (queue_fds != NULL)
+ *queue_fds = _dbus_counter_get_unix_fd_value (transport->live_messages);
+
+ if (peak_queue_bytes != NULL)
+ *peak_queue_bytes = _dbus_counter_get_peak_size_value (transport->live_messages);
+
+ if (peak_queue_fds != NULL)
+ *peak_queue_fds = _dbus_counter_get_peak_unix_fd_value (transport->live_messages);
+}
+#endif /* DBUS_ENABLE_STATS */
+
/** @} */
void _dbus_transport_set_allow_anonymous (DBusTransport *transport,
dbus_bool_t value);
+/* if DBUS_ENABLE_STATS */
+void _dbus_transport_get_stats (DBusTransport *transport,
+ dbus_uint32_t *queue_bytes,
+ dbus_uint32_t *queue_fds,
+ dbus_uint32_t *peak_queue_bytes,
+ dbus_uint32_t *peak_queue_fds);
DBUS_END_DECLS
void *data; /**< Application data. */
DBusFreeFunction free_data_function; /**< Free the application data. */
unsigned int enabled : 1; /**< Whether it's enabled. */
+ unsigned int oom_last_time : 1; /**< Whether it was OOM last time. */
};
dbus_bool_t
return watch->enabled;
}
+dbus_bool_t
+_dbus_watch_get_oom_last_time (DBusWatch *watch)
+{
+ return watch->oom_last_time;
+}
+
+void
+_dbus_watch_set_oom_last_time (DBusWatch *watch,
+ dbus_bool_t oom)
+{
+ watch->oom_last_time = oom;
+}
+
/**
* Creates a new DBusWatch. Used to add a file descriptor to be polled
* by a main loop.
watch->refcount -= 1;
if (watch->refcount == 0)
{
+ if (watch->fd != -1)
+ _dbus_warn ("this watch should have been invalidated");
+
dbus_watch_set_data (watch, NULL, NULL); /* call free_data_function */
if (watch->free_handler_data_function)
dbus_bool_t enabled);
dbus_bool_t _dbus_watch_get_enabled (DBusWatch *watch);
+dbus_bool_t _dbus_watch_get_oom_last_time (DBusWatch *watch);
+void _dbus_watch_set_oom_last_time (DBusWatch *watch,
+ dbus_bool_t oom);
+
/** @} */
DBUS_END_DECLS
<article id="index">
<articleinfo>
<title>D-Bus Specification</title>
- <releaseinfo>Version 0.15</releaseinfo>
- <date>3 November 2010</date>
+ <releaseinfo>Version 0.17</releaseinfo>
+ <date>2011-06-01</date>
<authorgroup>
<author>
<firstname>Havoc</firstname>
</address>
</affiliation>
</author>
+ <author>
+ <firstname>Simon</firstname>
+ <surname>McVittie</surname>
+ <affiliation>
+ <orgname>Collabora Ltd.</orgname>
+ <address>
+ <email>simon.mcvittie@collabora.co.uk</email>
+ </address>
+ </affiliation>
+ </author>
+ <author>
+ <firstname>David</firstname>
+ <surname>Zeuthen</surname>
+ <affiliation>
+ <orgname>Red Hat, Inc.</orgname>
+ <address>
+ <email>davidz@redhat.com</email>
+ </address>
+ </affiliation>
+ </author>
</authorgroup>
<revhistory>
<revision>
<revremark></revremark>
</revision>
<revision>
+ <revnumber>0.17</revnumber>
+ <date>1 June 2011</date>
+ <authorinitials>smcv/davidz</authorinitials>
+ <revremark>define ObjectManager; reserve extra pseudo-type-codes used
+ by GVariant</revremark>
+ </revision>
+ <revision>
+ <revnumber>0.16</revnumber>
+ <date>11 April 2011</date>
+ <authorinitials></authorinitials>
+ <revremark>add path_namespace, arg0namespace; argNpath matches object
+ paths</revremark>
+ </revision>
+ <revision>
<revnumber>0.15</revnumber>
<date>3 November 2010</date>
<authorinitials></authorinitials>
</row><row>
<entry><literal>STRUCT</literal></entry>
<entry>114 (ASCII 'r'), 40 (ASCII '('), 41 (ASCII ')')</entry>
- <entry>Struct</entry>
+ <entry>Struct; type code 114 'r' is reserved for use in
+ bindings and implementations to represent the general
+ concept of a struct, and must not appear in signatures
+ used on D-Bus.</entry>
</row><row>
<entry><literal>VARIANT</literal></entry>
<entry>118 (ASCII 'v') </entry>
</row><row>
<entry><literal>DICT_ENTRY</literal></entry>
<entry>101 (ASCII 'e'), 123 (ASCII '{'), 125 (ASCII '}') </entry>
- <entry>Entry in a dict or map (array of key-value pairs)</entry>
+ <entry>Entry in a dict or map (array of key-value pairs).
+ Type code 101 'e' is reserved for use in bindings and
+ implementations to represent the general concept of a
+ dict or dict-entry, and must not appear in signatures
+ used on D-Bus.</entry>
</row><row>
<entry><literal>UNIX_FD</literal></entry>
<entry>104 (ASCII 'h')</entry>
<entry>Unix file descriptor</entry>
</row>
+ <row>
+ <entry>(reserved)</entry>
+ <entry>109 (ASCII 'm')</entry>
+ <entry>Reserved for <ulink
+ url="https://bugs.freedesktop.org/show_bug.cgi?id=27857">a
+ 'maybe' type compatible with the one in GVariant</ulink>,
+ and must not appear in signatures used on D-Bus until
+ specified here</entry>
+ </row>
+ <row>
+ <entry>(reserved)</entry>
+ <entry>42 (ASCII '*')</entry>
+ <entry>Reserved for use in bindings/implementations to
+ represent any <firstterm>single complete type</firstterm>,
+ and must not appear in signatures used on D-Bus.</entry>
+ </row>
+ <row>
+ <entry>(reserved)</entry>
+ <entry>63 (ASCII '?')</entry>
+ <entry>Reserved for use in bindings/implementations to
+ represent any <firstterm>basic type</firstterm>, and must
+ not appear in signatures used on D-Bus.</entry>
+ </row>
+ <row>
+ <entry>(reserved)</entry>
+ <entry>64 (ASCII '@'), 38 (ASCII '&'),
+ 94 (ASCII '^')</entry>
+ <entry>Reserved for internal use by bindings/implementations,
+ and must not appear in signatures used on D-Bus.
+ GVariant uses these type-codes to encode calling
+ conventions.</entry>
+ </row>
</tbody>
</tgroup>
</informaltable>
annotation.
</para>
</sect2>
+
+ <sect2 id="standard-interfaces-objectmanager">
+ <title><literal>org.freedesktop.DBus.ObjectManager</literal></title>
+ <para>
+ An API can optionally make use of this interface for one or
+ more sub-trees of objects. The root of each sub-tree implements
+ this interface so other applications can get all objects,
+ interfaces and properties in a single method call. It is
+ appropriate to use this interface if users of the tree of
+ objects are expected to be interested in all interfaces of all
+ objects in the tree; a more granular API should be used if
+ users of the objects are expected to be interested in a small
+ subset of the objects, a small subset of their interfaces, or
+ both.
+ </para>
+ <para>
+ The method that applications can use to get all objects and
+ properties is <literal>GetManagedObjects</literal>:
+ </para>
+ <para>
+ <programlisting>
+ org.freedesktop.DBus.ObjectManager.GetManagedObjects (out DICT<OBJPATH,DICT<STRING,DICT<STRING,VARIANT>>> objpath_interfaces_and_properties);
+ </programlisting>
+ </para>
+ <para>
+ The return value of this method is a dict whose keys are
+ object paths. All returned object paths are children of the
+ object path implementing this interface, i.e. their object
+ paths start with the ObjectManager's object path plus '/'.
+ </para>
+ <para>
+ Each value is a dict whose keys are interfaces names. Each
+ value in this inner dict is the same dict that would be
+ returned by the <link
+ linkend="standard-interfaces-properties">org.freedesktop.DBus.Properties.GetAll()</link>
+ method for that combination of object path and interface. If
+ an interface has no properties, the empty dict is returned.
+ </para>
+ <para>
+ Changes are emitted using the following two signals:
+ </para>
+ <para>
+ <programlisting>
+ org.freedesktop.DBus.ObjectManager.InterfacesAdded (OBJPATH object_path,
+ DICT<STRING,DICT<STRING,VARIANT>> interfaces_and_properties);
+ org.freedesktop.DBus.ObjectManager.InterfacesRemoved (OBJPATH object_path,
+ ARRAY<STRING> interfaces);
+ </programlisting>
+ </para>
+ <para>
+ The <literal>InterfacesAdded</literal> signal is emitted when
+ either a new object is added or when an existing object gains
+ one or more interfaces. The
+ <literal>InterfacesRemoved</literal> signal is emitted
+ whenever an object is removed or it loses one or more
+ interfaces. The second parameter of the
+ <literal>InterfacesAdded</literal> signal contains a dict with
+ the interfaces and properties (if any) that have been added to
+ the given object path. Similarly, the second parameter of the
+ <literal>InterfacesRemoved</literal> signal contains an array
+ of the interfaces that were removed. Note that changes on
+ properties on existing interfaces are not reported using this
+ interface - an application should also monitor the existing <link
+ linkend="standard-interfaces-properties">PropertiesChanged</link>
+ signal on each object.
+ </para>
+ <para>
+ Applications SHOULD NOT export objects that are children of an
+ object (directly or otherwise) implementing this interface but
+ which are not returned in the reply from the
+ <literal>GetManagedObjects()</literal> method of this
+ interface on the given object.
+ </para>
+ <para>
+ The intent of the <literal>ObjectManager</literal> interface
+ is to make it easy to write a robust client
+ implementation. The trivial client implementation only needs
+ to make two method calls:
+ </para>
+ <para>
+ <programlisting>
+ org.freedesktop.DBus.AddMatch (bus_proxy,
+ "type='signal',name='org.example.App',path_namespace='/org/example/App'");
+ objects = org.freedesktop.DBus.ObjectManager.GetManagedObjects (app_proxy);
+ </programlisting>
+ </para>
+ <para>
+ on the message bus and the remote application's
+ <literal>ObjectManager</literal>, respectively. Whenever a new
+ remote object is created (or an existing object gains a new
+ interface), the <literal>InterfacesAdded</literal> signal is
+ emitted, and since this signal contains all properties for the
+ interfaces, no calls to the
+ <literal>org.freedesktop.Properties</literal> interface on the
+ remote object are needed. Additionally, since the initial
+ <literal>AddMatch()</literal> rule already includes signal
+ messages from the newly created child object, no new
+ <literal>AddMatch()</literal> call is needed.
+ </para>
+
+ <para>
+ <emphasis>
+ The <literal>org.freedesktop.DBus.ObjectManager</literal>
+ interface was added in version 0.17 of the D-Bus
+ specification.
+ </emphasis>
+ </para>
+ </sect2>
</sect1>
<sect1 id="introspection-format">
path match is path='/org/freedesktop/Hal/Manager'</entry>
</row>
<row>
+ <entry><literal>path_namespace</literal></entry>
+ <entry>An object path</entry>
+ <entry>
+ <para>
+ Matches messages which are sent from or to an
+ object for which the object path is either the
+ given value, or that value followed by one or
+ more path components.
+ </para>
+
+ <para>
+ For example,
+ <literal>path_namespace='/com/example/foo'</literal>
+ would match signals sent by
+ <literal>/com/example/foo</literal>
+ or by
+ <literal>/com/example/foo/bar</literal>,
+ but not by
+ <literal>/com/example/foobar</literal>.
+ </para>
+
+ <para>
+ Using both <literal>path</literal> and
+ <literal>path_namespace</literal> in the same match
+ rule is not allowed.
+ </para>
+
+ <para>
+ <emphasis>
+ This match key was added in version 0.16 of the
+ D-Bus specification and implemented by the bus
+ daemon in dbus 1.5.0 and later.
+ </emphasis>
+ </para>
+ </entry>
+ </row>
+ <row>
<entry><literal>destination</literal></entry>
<entry>A unique name (see <xref linkend="term-unique-name"/>)</entry>
<entry>Matches messages which are being sent to the given unique name. An
<entry><literal>arg[0, 1, 2, 3, ...]</literal></entry>
<entry>Any string</entry>
<entry>Arg matches are special and are used for further restricting the
- match based on the arguments in the body of a message. As of this time
- only string arguments can be matched. An example of an argument match
+ match based on the arguments in the body of a message. Only arguments of type
+ STRING can be matched in this way. An example of an argument match
would be arg3='Foo'. Only argument indexes from 0 to 63 should be
accepted.</entry>
</row>
<row>
<entry><literal>arg[0, 1, 2, 3, ...]path</literal></entry>
<entry>Any string</entry>
- <entry>Argument path matches provide a specialised form of wildcard
- matching for path-like namespaces. As with normal argument matches,
- if the argument is exactly equal to the string given in the match
- rule then the rule is satisfied. Additionally, there is also a
- match when either the string given in the match rule or the
- appropriate message argument ends with '/' and is a prefix of the
- other. An example argument path match is arg0path='/aa/bb/'. This
- would match messages with first arguments of '/', '/aa/',
- '/aa/bb/', '/aa/bb/cc/' and '/aa/bb/cc'. It would not match
- messages with first arguments of '/aa/b', '/aa' or even '/aa/bb'.</entry>
+ <entry>
+ <para>Argument path matches provide a specialised form of wildcard matching for
+ path-like namespaces. They can match arguments whose type is either STRING or
+ OBJECT_PATH. As with normal argument matches,
+ if the argument is exactly equal to the string given in the match
+ rule then the rule is satisfied. Additionally, there is also a
+ match when either the string given in the match rule or the
+ appropriate message argument ends with '/' and is a prefix of the
+ other. An example argument path match is arg0path='/aa/bb/'. This
+ would match messages with first arguments of '/', '/aa/',
+ '/aa/bb/', '/aa/bb/cc/' and '/aa/bb/cc'. It would not match
+ messages with first arguments of '/aa/b', '/aa' or even '/aa/bb'.</para>
+
+ <para>This is intended for monitoring “directories” in file system-like
+ hierarchies, as used in the <citetitle>dconf</citetitle> configuration
+ system. An application interested in all nodes in a particular hierarchy would
+ monitor <literal>arg0path='/ca/example/foo/'</literal>. Then the service could
+ emit a signal with zeroth argument <literal>"/ca/example/foo/bar"</literal> to
+ represent a modification to the “bar” property, or a signal with zeroth
+ argument <literal>"/ca/example/"</literal> to represent atomic modification of
+ many properties within that directory, and the interested application would be
+ notified in both cases.</para>
+ <para>
+ <emphasis>
+ This match key was added in version 0.12 of the
+ D-Bus specification, implemented for STRING
+ arguments by the bus daemon in dbus 1.2.0 and later,
+ and implemented for OBJECT_PATH arguments in dbus 1.5.0
+ and later.
+ </emphasis>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry><literal>arg0namespace</literal></entry>
+ <entry>Like a bus name, except that the string is not
+ required to contain a '.' (period)</entry>
+ <entry>
+ <para>Match messages whose first argument is of type STRING, and is a bus name
+ or interface name within the specified namespace. This is primarily intended
+ for watching name owner changes for a group of related bus names, rather than
+ for a single name or all name changes.</para>
+
+ <para>Because every valid interface name is also a valid
+ bus name, this can also be used for messages whose
+ first argument is an interface name.</para>
+
+ <para>For example, the match rule
+ <literal>member='NameOwnerChanged',arg0namespace='com.example.backend'</literal>
+ matches name owner changes for bus names such as
+ <literal>com.example.backend.foo</literal>,
+ <literal>com.example.backend.foo.bar</literal>, and
+ <literal>com.example.backend</literal> itself.</para>
+
+ <para>See also <xref linkend='bus-messages-name-owner-changed'/>.</para>
+ <para>
+ <emphasis>
+ This match key was added in version 0.16 of the
+ D-Bus specification and implemented by the bus
+ daemon in dbus 1.5.0 and later.
+ </emphasis>
+ </para>
+ </entry>
</row>
</tbody>
</tgroup>
installable_tests = \
test-corrupt \
test-dbus-daemon \
+ test-dbus-daemon-eavesdrop \
test-loopback \
test-marshal \
test-relay \
$(GLIB_LIBS) \
$(DBUS_GLIB_LIBS)
+test_dbus_daemon_eavesdrop_SOURCES = dbus-daemon-eavesdrop.c
+test_dbus_daemon_eavesdrop_CPPFLAGS = $(GLIB_CFLAGS) $(DBUS_GLIB_CFLAGS)
+test_dbus_daemon_eavesdrop_LDFLAGS = @R_DYNAMIC_LDFLAG@
+test_dbus_daemon_eavesdrop_LDADD = $(top_builddir)/dbus/libdbus-1.la \
+ $(GLIB_LIBS) \
+ $(DBUS_GLIB_LIBS)
+
test_marshal_SOURCES = marshal.c
test_marshal_CPPFLAGS = $(GLIB_CFLAGS) $(DBUS_GLIB_CFLAGS)
test_marshal_LDFLAGS = @R_DYNAMIC_LDFLAG@
{
int b;
b = _dbus_string_get_byte (mutated, i);
- if (_dbus_type_is_valid (b))
+ if (dbus_type_is_valid (b))
{
_dbus_string_set_byte (mutated, i, random_type ());
return;
--- /dev/null
+/* Integration tests for the eavesdrop=true|false keyword in DBusMatchRule
+ *
+ * Author: Cosimo Alfarano <cosimo.alfarano@collabora.co.uk>
+ * Based on: tests/dbus-daemon.c by Simon McVittie
+ * Copyright © 2010-2011 Nokia Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <config.h>
+
+#include <glib.h>
+
+#include <dbus/dbus.h>
+#include <dbus/dbus-glib-lowlevel.h>
+
+#include <string.h>
+
+#ifdef DBUS_WIN
+# include <windows.h>
+#else
+# include <signal.h>
+# include <unistd.h>
+#endif
+
+#define SENDER_NAME "test.eavesdrop.sender"
+#define SENDER_PATH "/test/eavesdrop/sender"
+#define SENDER_IFACE SENDER_NAME
+#define SENDER_SIGNAL_NAME "Signal"
+#define SENDER_STOPPER_NAME "Stopper"
+
+/* This rule is equivalent to the one added to a proxy connecting to
+ * SENDER_NAME+SENDER_IFACE, plus restricting on signal name.
+ * Being more restrictive, if the connection receives what we need, for sure
+ * the original proxy rule will match it */
+#define RECEIVER_RULE "sender='" SENDER_NAME "'," \
+ "interface='" SENDER_IFACE "'," \
+ "type='signal'," \
+ "member='" SENDER_SIGNAL_NAME "'"
+#define POLITELISTENER_RULE RECEIVER_RULE
+#define EAVESDROPPER_RULE RECEIVER_RULE ",eavesdrop=true"
+
+#define STOPPER_RULE "sender='" SENDER_NAME \
+ "',interface='" SENDER_IFACE "',type='signal',member='" SENDER_STOPPER_NAME "'"
+
+/* a connection received a signal to whom? */
+typedef enum {
+ NONE_YET = 0,
+ TO_ME,
+ TO_OTHER,
+ BROADCAST,
+} SignalDst;
+
+typedef struct {
+ DBusError e;
+ GError *ge;
+
+ gint daemon_pid;
+
+ /* eavedrop keyword tests */
+ DBusConnection *sender;
+ DBusConnection *receiver;
+ SignalDst receiver_dst;
+ dbus_bool_t receiver_got_stopper;
+ DBusConnection *eavesdropper;
+ SignalDst eavesdropper_dst;
+ dbus_bool_t eavesdropper_got_stopper;
+ DBusConnection *politelistener;
+ SignalDst politelistener_dst;
+ dbus_bool_t politelistener_got_stopper;
+} Fixture;
+
+#define assert_no_error(e) _assert_no_error (e, __FILE__, __LINE__)
+static void
+_assert_no_error (const DBusError *e,
+ const char *file,
+ int line)
+{
+ if (G_UNLIKELY (dbus_error_is_set (e)))
+ g_error ("%s:%d: expected success but got error: %s: %s",
+ file, line, e->name, e->message);
+}
+
+static gchar *
+spawn_dbus_daemon (gchar *binary,
+ gchar *configuration,
+ gint *daemon_pid)
+{
+ GError *error = NULL;
+ GString *address;
+ gint address_fd;
+ gchar *argv[] = {
+ binary,
+ configuration,
+ "--nofork",
+ "--print-address=1", /* stdout */
+ NULL
+ };
+
+ g_spawn_async_with_pipes (NULL, /* working directory */
+ argv,
+ NULL, /* envp */
+ G_SPAWN_DO_NOT_REAP_CHILD | G_SPAWN_SEARCH_PATH,
+ NULL, /* child_setup */
+ NULL, /* user data */
+ daemon_pid,
+ NULL, /* child's stdin = /dev/null */
+ &address_fd,
+ NULL, /* child's stderr = our stderr */
+ &error);
+ g_assert_no_error (error);
+
+ address = g_string_new (NULL);
+
+ /* polling until the dbus-daemon writes out its address is a bit stupid,
+ * but at least it's simple, unlike dbus-launch... in principle we could
+ * use select() here, but life's too short */
+ while (1)
+ {
+ gssize bytes;
+ gchar buf[4096];
+ gchar *newline;
+
+ bytes = read (address_fd, buf, sizeof (buf));
+
+ if (bytes > 0)
+ g_string_append_len (address, buf, bytes);
+
+ newline = strchr (address->str, '\n');
+
+ if (newline != NULL)
+ {
+ g_string_truncate (address, newline - address->str);
+ break;
+ }
+
+ g_usleep (G_USEC_PER_SEC / 10);
+ }
+
+ return g_string_free (address, FALSE);
+}
+
+static DBusConnection *
+connect_to_bus (const gchar *address)
+{
+ DBusConnection *conn;
+ DBusError error = DBUS_ERROR_INIT;
+ dbus_bool_t ok;
+
+ conn = dbus_connection_open_private (address, &error);
+ assert_no_error (&error);
+ g_assert (conn != NULL);
+
+ ok = dbus_bus_register (conn, &error);
+ assert_no_error (&error);
+ g_assert (ok);
+ g_assert (dbus_bus_get_unique_name (conn) != NULL);
+
+ dbus_connection_setup_with_g_main (conn, NULL);
+ return conn;
+}
+
+/* send a unicast signal to <self> to ensure that no other connection
+ * listening is the actual recipient for the signal */
+static DBusHandlerResult
+sender_send_unicast_to_sender (Fixture *f)
+{
+ DBusError error = DBUS_ERROR_INIT;
+ DBusMessage *signal;
+
+ signal = dbus_message_new_signal (SENDER_PATH, SENDER_IFACE,
+ SENDER_SIGNAL_NAME);
+ dbus_message_set_destination (signal, dbus_bus_get_unique_name (f->sender));
+
+ if (signal == NULL)
+ g_error ("OOM");
+
+ if (!dbus_connection_send (f->sender, signal, NULL))
+ g_error ("OOM");
+
+ dbus_message_unref (signal);
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+/* send a unicast signal to <receiver>, making <politelistener> and
+ * <eavesdropper> not a actual recipient for it */
+static DBusHandlerResult
+sender_send_unicast_to_receiver (Fixture *f)
+{
+ DBusError error = DBUS_ERROR_INIT;
+ DBusMessage *signal;
+
+ signal = dbus_message_new_signal (SENDER_PATH, SENDER_IFACE, SENDER_SIGNAL_NAME);
+ dbus_message_set_destination (signal, dbus_bus_get_unique_name (f->receiver));
+
+ if (signal == NULL)
+ g_error ("OOM");
+
+ if (!dbus_connection_send (f->sender, signal, NULL))
+ g_error ("OOM");
+
+ dbus_message_unref (signal);
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+static DBusHandlerResult
+sender_send_broadcast (Fixture *f)
+{
+ DBusError error = DBUS_ERROR_INIT;
+ DBusMessage *signal;
+
+ signal = dbus_message_new_signal (SENDER_PATH, SENDER_IFACE, SENDER_SIGNAL_NAME);
+ dbus_message_set_destination (signal, NULL);
+
+ if (signal == NULL)
+ g_error ("OOM");
+
+ if (!dbus_connection_send (f->sender, signal, NULL))
+ g_error ("OOM");
+
+ dbus_message_unref (signal);
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+/* Send special broadcast signal to indicate that the connections can "stop"
+ * listening and check their results.
+ * DBus does not re-order messages, so when the three connections have received
+ * this signal, we are sure that any message sent before it has also been
+ * dispatched. */
+static DBusHandlerResult
+sender_send_stopper (Fixture *f)
+{
+ DBusError error = DBUS_ERROR_INIT;
+ DBusMessage *signal;
+
+ signal = dbus_message_new_signal (SENDER_PATH, SENDER_IFACE, SENDER_STOPPER_NAME);
+ dbus_message_set_destination (signal, NULL);
+
+ if (signal == NULL)
+ g_error ("OOM");
+
+ if (!dbus_connection_send (f->sender, signal, NULL))
+ g_error ("OOM");
+
+ dbus_message_unref (signal);
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+/* Ignore NameAcquired, then depending on the signal received:
+ * - updates f-><conn>_dst based on the destination of the message
+ * - asserts that <conn> received the stop signal
+ */
+static DBusHandlerResult
+signal_filter (DBusConnection *connection,
+ DBusMessage *message,
+ void *user_data)
+{
+ Fixture *f = user_data;
+ SignalDst *dst = NULL;
+ DBusConnection **conn;
+ dbus_bool_t *got_stopper;
+
+ if (0 == strcmp (dbus_message_get_member (message), "NameAcquired"))
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+ if (connection == f->receiver)
+ {
+ dst = &(f->receiver_dst);
+ conn = &(f->receiver);
+ got_stopper = &(f->receiver_got_stopper);
+ }
+ else if (connection == f->eavesdropper)
+ {
+ dst = &(f->eavesdropper_dst);
+ conn = &(f->eavesdropper);
+ got_stopper = &(f->eavesdropper_got_stopper);
+ }
+ else if (connection == f->politelistener)
+ {
+ dst = &(f->politelistener_dst);
+ conn = &(f->politelistener);
+ got_stopper = &(f->politelistener_got_stopper);
+ }
+ else
+ {
+ g_error ("connection not matching");
+ }
+
+ if (0 == strcmp (dbus_message_get_member (message), SENDER_SIGNAL_NAME))
+ {
+ if (dbus_message_get_destination (message) == NULL)
+ *dst = BROADCAST;
+ else if (0 == strcmp (dbus_message_get_destination (message), dbus_bus_get_unique_name (*conn)))
+ *dst = TO_ME;
+ else /* if (dbus_message_get_destination (message) != NULL) */
+ *dst = TO_OTHER;
+ }
+ else if (0 == strcmp (dbus_message_get_member (message), SENDER_STOPPER_NAME))
+ {
+ *got_stopper = TRUE;
+ }
+ else
+ {
+ g_error ("got unknown member from message: %s",
+ dbus_message_get_member (message));
+ }
+
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+static void
+add_receiver_filter (Fixture *f)
+{
+ DBusError e = DBUS_ERROR_INIT;
+
+ dbus_bus_add_match (f->receiver, RECEIVER_RULE, &e);
+ assert_no_error (&e);
+ dbus_bus_add_match (f->receiver, STOPPER_RULE, &e);
+ assert_no_error (&e);
+
+ if (!dbus_connection_add_filter (f->receiver,
+ signal_filter, f, NULL))
+ g_error ("OOM");
+}
+
+static void
+add_eavesdropper_filter (Fixture *f)
+{
+ DBusError e = DBUS_ERROR_INIT;
+
+ dbus_bus_add_match (f->eavesdropper, EAVESDROPPER_RULE, &e);
+ assert_no_error (&e);
+ dbus_bus_add_match (f->eavesdropper, STOPPER_RULE, &e);
+ assert_no_error (&e);
+
+ if (!dbus_connection_add_filter (f->eavesdropper,
+ signal_filter, f, NULL))
+ g_error ("OOM");
+}
+
+static void
+add_politelistener_filter (Fixture *f)
+{
+ DBusError e = DBUS_ERROR_INIT;
+
+ dbus_bus_add_match (f->politelistener, POLITELISTENER_RULE, &e);
+ assert_no_error (&e);
+ dbus_bus_add_match (f->politelistener, STOPPER_RULE, &e);
+ assert_no_error (&e);
+
+ if (!dbus_connection_add_filter (f->politelistener,
+ signal_filter, f, NULL))
+ g_error ("OOM");
+}
+
+static void
+setup (Fixture *f,
+ gconstpointer context G_GNUC_UNUSED)
+{
+ gchar *dbus_daemon;
+ gchar *config;
+ gchar *address;
+
+ f->ge = NULL;
+ dbus_error_init (&f->e);
+
+ dbus_daemon = g_strdup (g_getenv ("DBUS_TEST_DAEMON"));
+
+ if (dbus_daemon == NULL)
+ dbus_daemon = g_strdup ("dbus-daemon");
+
+ if (g_getenv ("DBUS_TEST_SYSCONFDIR") != NULL)
+ {
+ config = g_strdup_printf ("--config-file=%s/dbus-1/session.conf",
+ g_getenv ("DBUS_TEST_SYSCONFDIR"));
+ }
+ else if (g_getenv ("DBUS_TEST_DATA") != NULL)
+ {
+ config = g_strdup_printf (
+ "--config-file=%s/valid-config-files/session.conf",
+ g_getenv ("DBUS_TEST_DATA"));
+ }
+ else
+ {
+ config = g_strdup ("--session");
+ }
+
+ address = spawn_dbus_daemon (dbus_daemon, config, &f->daemon_pid);
+
+ g_free (dbus_daemon);
+ g_free (config);
+
+ f->sender = connect_to_bus (address);
+ dbus_bus_request_name (f->sender, SENDER_NAME, DBUS_NAME_FLAG_DO_NOT_QUEUE,
+ &(f->e));
+ f->receiver = connect_to_bus (address);
+ f->eavesdropper = connect_to_bus (address);
+ f->politelistener = connect_to_bus (address);
+ add_receiver_filter (f);
+ add_politelistener_filter (f);
+ add_eavesdropper_filter (f);
+
+ g_free (address);
+}
+
+static void
+test_eavesdrop_broadcast (Fixture *f,
+ gconstpointer context G_GNUC_UNUSED)
+{
+ sender_send_broadcast (f);
+ sender_send_stopper (f);
+
+ while (!f->receiver_got_stopper ||
+ !f->politelistener_got_stopper ||
+ !f->eavesdropper_got_stopper)
+ g_main_context_iteration (NULL, TRUE);
+
+ /* all the three connection can receive a broadcast */
+ g_assert_cmpint (f->receiver_dst, ==, BROADCAST);
+ g_assert_cmpint (f->politelistener_dst, ==, BROADCAST);
+ g_assert_cmpint (f->eavesdropper_dst, ==, BROADCAST);
+}
+
+/* a way to say that none of the listening connection are destination of the
+ * signal */
+static void
+test_eavesdrop_unicast_to_sender (Fixture *f,
+ gconstpointer context G_GNUC_UNUSED)
+{
+ sender_send_unicast_to_sender (f);
+ sender_send_stopper (f);
+
+ while (!f->receiver_got_stopper ||
+ !f->politelistener_got_stopper ||
+ !f->eavesdropper_got_stopper)
+ g_main_context_iteration (NULL, TRUE);
+
+ /* not directed to it and not broadcasted, they cannot receive it */
+ g_assert_cmpint (f->receiver_dst, ==, NONE_YET);
+ g_assert_cmpint (f->politelistener_dst, ==, NONE_YET);
+ /* eavesdrop=true, it will receive the signal even though it's not directed
+ * to it */
+ g_assert_cmpint (f->eavesdropper_dst, ==, TO_OTHER);
+}
+
+static void
+test_eavesdrop_unicast_to_receiver (Fixture *f,
+ gconstpointer context G_GNUC_UNUSED)
+{
+ sender_send_unicast_to_receiver (f);
+ sender_send_stopper (f);
+
+ while (!f->receiver_got_stopper ||
+ !f->politelistener_got_stopper ||
+ !f->eavesdropper_got_stopper)
+ g_main_context_iteration (NULL, TRUE);
+
+ /* direct to him */
+ g_assert_cmpint (f->receiver_dst, ==, TO_ME);
+ /* not directed to it and not broadcasted, it cannot receive it */
+ g_assert_cmpint (f->politelistener_dst, ==, NONE_YET);
+ /* eavesdrop=true, it will receive the signal even though it's not directed
+ * to it */
+ g_assert_cmpint (f->eavesdropper_dst, ==, TO_OTHER);
+}
+
+static void
+teardown (Fixture *f,
+ gconstpointer context G_GNUC_UNUSED)
+{
+ dbus_error_free (&f->e);
+ g_clear_error (&f->ge);
+
+ if (f->sender != NULL)
+ {
+ dbus_connection_close (f->sender);
+ dbus_connection_unref (f->sender);
+ f->sender = NULL;
+ }
+
+ if (f->receiver != NULL)
+ {
+ dbus_connection_remove_filter (f->receiver,
+ signal_filter, f);
+
+ dbus_connection_close (f->receiver);
+ dbus_connection_unref (f->receiver);
+ f->receiver = NULL;
+ }
+
+ if (f->politelistener != NULL)
+ {
+ dbus_connection_remove_filter (f->politelistener,
+ signal_filter, f);
+
+ dbus_connection_close (f->politelistener);
+ dbus_connection_unref (f->politelistener);
+ f->politelistener = NULL;
+ }
+
+ if (f->eavesdropper != NULL)
+ {
+ dbus_connection_remove_filter (f->eavesdropper,
+ signal_filter, f);
+
+ dbus_connection_close (f->eavesdropper);
+ dbus_connection_unref (f->eavesdropper);
+ f->eavesdropper = NULL;
+ }
+
+#ifdef DBUS_WIN
+ TerminateProcess (f->daemon_pid, 1);
+#else
+ kill (f->daemon_pid, SIGTERM);
+#endif
+
+ g_spawn_close_pid (f->daemon_pid);
+}
+
+int
+main (int argc,
+ char **argv)
+{
+ g_test_init (&argc, &argv, NULL);
+ g_test_bug_base ("https://bugs.freedesktop.org/show_bug.cgi?id=");
+
+ g_test_add ("/eavedrop/match_keyword/broadcast", Fixture, NULL,
+ setup, test_eavesdrop_broadcast, teardown);
+ g_test_add ("/eavedrop/match_keyword/unicast_to_receiver", Fixture, NULL,
+ setup, test_eavesdrop_unicast_to_receiver,
+ teardown);
+ g_test_add ("/eavedrop/match_keyword/unicast_to_sender", Fixture, NULL,
+ setup, test_eavesdrop_unicast_to_sender, teardown);
+
+ return g_test_run ();
+}
} CData;
static dbus_bool_t
-connection_watch_callback (DBusWatch *watch,
- unsigned int condition,
- void *data)
-{
- return dbus_watch_handle (watch, condition);
-}
-
-static dbus_bool_t
add_watch (DBusWatch *watch,
void *data)
{
CData *cd = data;
- return _dbus_loop_add_watch (cd->loop,
- watch,
- connection_watch_callback,
- cd, NULL);
+ return _dbus_loop_add_watch (cd->loop, watch);
}
static void
{
CData *cd = data;
- _dbus_loop_remove_watch (cd->loop,
- watch, connection_watch_callback, cd);
-}
-
-static void
-connection_timeout_callback (DBusTimeout *timeout,
- void *data)
-{
- /* Can return FALSE on OOM but we just let it fire again later */
- dbus_timeout_handle (timeout);
+ _dbus_loop_remove_watch (cd->loop, watch);
}
static dbus_bool_t
{
CData *cd = data;
- return _dbus_loop_add_timeout (cd->loop,
- timeout, connection_timeout_callback, cd, NULL);
+ return _dbus_loop_add_timeout (cd->loop, timeout);
}
static void
{
CData *cd = data;
- _dbus_loop_remove_timeout (cd->loop,
- timeout, connection_timeout_callback, cd);
+ _dbus_loop_remove_timeout (cd->loop, timeout);
}
static void
}
static dbus_bool_t
-server_watch_callback (DBusWatch *watch,
- unsigned int condition,
- void *data)
-{
- /* FIXME this can be done in dbus-mainloop.c
- * if the code in activation.c for the babysitter
- * watch handler is fixed.
- */
-
- return dbus_watch_handle (watch, condition);
-}
-
-static dbus_bool_t
add_server_watch (DBusWatch *watch,
void *data)
{
ServerData *context = data;
- return _dbus_loop_add_watch (context->loop,
- watch, server_watch_callback, context,
- NULL);
+ return _dbus_loop_add_watch (context->loop, watch);
}
static void
{
ServerData *context = data;
- _dbus_loop_remove_watch (context->loop,
- watch, server_watch_callback, context);
-}
-
-static void
-server_timeout_callback (DBusTimeout *timeout,
- void *data)
-{
- /* can return FALSE on OOM but we just let it fire again later */
- dbus_timeout_handle (timeout);
+ _dbus_loop_remove_watch (context->loop, watch);
}
static dbus_bool_t
{
ServerData *context = data;
- return _dbus_loop_add_timeout (context->loop,
- timeout, server_timeout_callback, context, NULL);
+ return _dbus_loop_add_timeout (context->loop, timeout);
}
static void
{
ServerData *context = data;
- _dbus_loop_remove_timeout (context->loop,
- timeout, server_timeout_callback, context);
+ _dbus_loop_remove_timeout (context->loop, timeout);
}
dbus_bool_t
#include "dbus-print-message.h"
+#define EAVESDROPPING_RULE "eavesdrop=true"
+
#ifdef DBUS_WIN
/* gettimeofday is not defined on windows */
}
#endif
+inline static void
+oom (const char *doing)
+{
+ fprintf (stderr, "OOM while %s\n", doing);
+ exit (1);
+}
+
static DBusHandlerResult
monitor_filter_func (DBusConnection *connection,
DBusMessage *message,
else if (arg[0] == '-')
usage (argv[0], 1);
else {
- numFilters++;
- filters = (char **)realloc(filters, numFilters * sizeof(char *));
- filters[j] = (char *)malloc((strlen(arg) + 1) * sizeof(char *));
- snprintf(filters[j], strlen(arg) + 1, "%s", arg);
- j++;
+ unsigned int filter_len;
+ numFilters++;
+ /* Prepend a rule (and a comma) to enable the monitor to eavesdrop.
+ * Prepending allows the user to add eavesdrop=false at command line
+ * in order to disable eavesdropping when needed */
+ filter_len = strlen (EAVESDROPPING_RULE) + 1 + strlen (arg) + 1;
+
+ filters = (char **) realloc (filters, numFilters * sizeof (char *));
+ if (filters == NULL)
+ oom ("adding a new filter slot");
+ filters[j] = (char *) malloc (filter_len * sizeof (char *));
+ if (filters[j] == NULL)
+ oom ("adding a new filter");
+ snprintf (filters[j], filter_len, "%s,%s", EAVESDROPPING_RULE, arg);
+ j++;
}
}
else
{
dbus_bus_add_match (connection,
- "type='signal'",
+ EAVESDROPPING_RULE ",type='signal'",
&error);
if (dbus_error_is_set (&error))
goto lose;
dbus_bus_add_match (connection,
- "type='method_call'",
+ EAVESDROPPING_RULE ",type='method_call'",
&error);
if (dbus_error_is_set (&error))
goto lose;
dbus_bus_add_match (connection,
- "type='method_return'",
+ EAVESDROPPING_RULE ",type='method_return'",
&error);
if (dbus_error_is_set (&error))
goto lose;
dbus_bus_add_match (connection,
- "type='error'",
+ EAVESDROPPING_RULE ",type='error'",
&error);
if (dbus_error_is_set (&error))
goto lose;