ofono/gdbus/mainloop.c

372 lines
7.8 KiB
C
Raw Normal View History

/*
*
* D-Bus helper library
*
2011-01-02 01:31:09 +00:00
* Copyright (C) 2004-2011 Marcel Holtmann <marcel@holtmann.org>
*
*
* 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 St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <glib.h>
#include <dbus/dbus.h>
#include "gdbus.h"
#define info(fmt...)
#define error(fmt...)
#define debug(fmt...)
2010-01-03 05:42:10 +00:00
struct timeout_handler {
guint id;
DBusTimeout *timeout;
2010-01-03 05:42:10 +00:00
};
struct watch_info {
2010-01-03 05:42:10 +00:00
guint id;
DBusWatch *watch;
DBusConnection *conn;
};
struct disconnect_data {
2010-01-03 05:42:10 +00:00
GDBusWatchFunction function;
void *user_data;
};
static gboolean disconnected_signal(DBusConnection *conn,
DBusMessage *msg, void *data)
{
struct disconnect_data *dc_data = data;
error("Got disconnected from the system message bus");
2010-01-03 05:42:10 +00:00
dc_data->function(conn, dc_data->user_data);
dbus_connection_unref(conn);
return TRUE;
}
2010-01-03 05:42:10 +00:00
static gboolean message_dispatch(void *data)
{
2010-01-03 05:42:10 +00:00
DBusConnection *conn = data;
/* Dispatch messages */
2010-01-03 05:42:10 +00:00
while (dbus_connection_dispatch(conn) == DBUS_DISPATCH_DATA_REMAINS);
2010-01-03 05:42:10 +00:00
dbus_connection_unref(conn);
return FALSE;
}
2010-01-03 05:42:10 +00:00
static inline void queue_dispatch(DBusConnection *conn,
DBusDispatchStatus status)
{
if (status == DBUS_DISPATCH_DATA_REMAINS)
g_idle_add(message_dispatch, dbus_connection_ref(conn));
2010-01-03 05:42:10 +00:00
}
static gboolean watch_func(GIOChannel *chan, GIOCondition cond, gpointer data)
{
2010-01-03 05:42:10 +00:00
struct watch_info *info = data;
unsigned int flags = 0;
DBusDispatchStatus status;
if (cond & G_IO_IN) flags |= DBUS_WATCH_READABLE;
if (cond & G_IO_OUT) flags |= DBUS_WATCH_WRITABLE;
if (cond & G_IO_HUP) flags |= DBUS_WATCH_HANGUP;
if (cond & G_IO_ERR) flags |= DBUS_WATCH_ERROR;
2010-01-03 05:42:10 +00:00
dbus_watch_handle(info->watch, flags);
gdbus: Fix incorrect DBusConnection reference counting Commit abfc2b0dd5c3e33abfdf1a815b16d492c1751c06 attempted to fix a crash related to improper reference counting, but the main issue was that the reference was taken only during the function call (which is usually unnecessary for single thread), but still passed a pointer to DBusConnection to a function that is called by the mainloop. This left a window where the DBusConnection can be destroyed. Fixes this crash on unit/test-gdbus-client: ==32642== Invalid read of size 4 ==32642== at 0x690D0A6: dbus_connection_ref (in /lib/i386-linux-gnu/libdbus-1.so.3.7.6) ==32642== by 0x804CEDB: message_dispatch (mainloop.c:73) ==32642== by 0x684580E: g_timeout_dispatch (gmain.c:4450) ==32642== by 0x6844A75: g_main_context_dispatch (gmain.c:3065) ==32642== by 0x6844E14: g_main_context_iterate.isra.23 (gmain.c:3712) ==32642== by 0x68452FA: g_main_loop_run (gmain.c:3906) ==32642== by 0x804C7D3: client_connect_disconnect (test-gdbus-client.c:188) ==32642== by 0x6868DB2: g_test_run_suite_internal (gtestutils.c:2067) ==32642== by 0x6868F8D: g_test_run_suite_internal (gtestutils.c:2138) ==32642== by 0x6869320: g_test_run_suite (gtestutils.c:2189) ==32642== by 0x686936B: g_test_run (gtestutils.c:1508) ==32642== by 0x696D4D2: (below main) (libc-start.c:226) ==32642== Address 0x709c6e4 is 140 bytes inside a block of size 144 free'd ==32642== at 0x67E806C: free (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so) ==32642== by 0x692D62E: dbus_free (in /lib/i386-linux-gnu/libdbus-1.so.3.7.6) ==32642== by 0x690E1C2: ??? (in /lib/i386-linux-gnu/libdbus-1.so.3.7.6) ==32642== by 0x804AAEC: destroy_context (test-gdbus-client.c:104) ==32642== by 0x6868DB2: g_test_run_suite_internal (gtestutils.c:2067) ==32642== by 0x6868F8D: g_test_run_suite_internal (gtestutils.c:2138) ==32642== by 0x6869320: g_test_run_suite (gtestutils.c:2189) ==32642== by 0x686936B: g_test_run (gtestutils.c:1508) ==32642== by 0x696D4D2: (below main) (libc-start.c:226)
2014-02-18 16:45:17 +00:00
status = dbus_connection_get_dispatch_status(info->conn);
queue_dispatch(info->conn, status);
return TRUE;
}
2010-01-03 05:42:10 +00:00
static void watch_info_free(void *data)
{
struct watch_info *info = data;
if (info->id > 0) {
g_source_remove(info->id);
info->id = 0;
}
dbus_connection_unref(info->conn);
g_free(info);
}
static dbus_bool_t add_watch(DBusWatch *watch, void *data)
{
DBusConnection *conn = data;
2010-01-03 05:42:10 +00:00
GIOCondition cond = G_IO_HUP | G_IO_ERR;
GIOChannel *chan;
struct watch_info *info;
2010-01-03 05:42:10 +00:00
unsigned int flags;
int fd;
if (!dbus_watch_get_enabled(watch))
return TRUE;
2010-01-03 05:42:10 +00:00
info = g_new0(struct watch_info, 1);
fd = dbus_watch_get_unix_fd(watch);
2010-01-03 05:42:10 +00:00
chan = g_io_channel_unix_new(fd);
info->watch = watch;
info->conn = dbus_connection_ref(conn);
2010-01-03 05:42:10 +00:00
dbus_watch_set_data(watch, info, watch_info_free);
flags = dbus_watch_get_flags(watch);
if (flags & DBUS_WATCH_READABLE) cond |= G_IO_IN;
if (flags & DBUS_WATCH_WRITABLE) cond |= G_IO_OUT;
2010-01-03 05:42:10 +00:00
info->id = g_io_add_watch(chan, cond, watch_func, info);
g_io_channel_unref(chan);
return TRUE;
}
static void remove_watch(DBusWatch *watch, void *data)
{
2010-01-03 05:42:10 +00:00
if (dbus_watch_get_enabled(watch))
return;
2010-01-03 05:42:10 +00:00
/* will trigger watch_info_free() */
dbus_watch_set_data(watch, NULL, NULL);
}
static void watch_toggled(DBusWatch *watch, void *data)
{
/* Because we just exit on OOM, enable/disable is
* no different from add/remove */
if (dbus_watch_get_enabled(watch))
add_watch(watch, data);
else
remove_watch(watch, data);
}
static gboolean timeout_handler_dispatch(gpointer data)
{
2010-01-03 05:42:10 +00:00
struct timeout_handler *handler = data;
handler->id = 0;
/* if not enabled should not be polled by the main loop */
2010-01-03 05:42:10 +00:00
if (!dbus_timeout_get_enabled(handler->timeout))
return FALSE;
dbus_timeout_handle(handler->timeout);
return FALSE;
}
static void timeout_handler_free(void *data)
{
2010-01-03 05:42:10 +00:00
struct timeout_handler *handler = data;
if (handler->id > 0) {
g_source_remove(handler->id);
handler->id = 0;
}
g_free(handler);
}
static dbus_bool_t add_timeout(DBusTimeout *timeout, void *data)
{
2010-01-03 05:42:10 +00:00
int interval = dbus_timeout_get_interval(timeout);
struct timeout_handler *handler;
if (!dbus_timeout_get_enabled(timeout))
return TRUE;
2010-01-03 05:42:10 +00:00
handler = g_new0(struct timeout_handler, 1);
handler->timeout = timeout;
dbus_timeout_set_data(timeout, handler, timeout_handler_free);
2010-01-03 05:42:10 +00:00
handler->id = g_timeout_add(interval, timeout_handler_dispatch,
handler);
return TRUE;
}
static void remove_timeout(DBusTimeout *timeout, void *data)
{
2010-01-03 05:42:10 +00:00
/* will trigger timeout_handler_free() */
dbus_timeout_set_data(timeout, NULL, NULL);
}
static void timeout_toggled(DBusTimeout *timeout, void *data)
{
if (dbus_timeout_get_enabled(timeout))
add_timeout(timeout, data);
else
remove_timeout(timeout, data);
}
2010-01-03 05:42:10 +00:00
static void dispatch_status(DBusConnection *conn,
DBusDispatchStatus status, void *data)
{
if (!dbus_connection_get_is_connected(conn))
return;
2010-01-03 05:42:10 +00:00
queue_dispatch(conn, status);
}
2010-01-03 05:42:10 +00:00
static inline void setup_dbus_with_main_loop(DBusConnection *conn)
{
dbus_connection_set_watch_functions(conn, add_watch, remove_watch,
watch_toggled, conn, NULL);
dbus_connection_set_timeout_functions(conn, add_timeout, remove_timeout,
2010-01-03 05:42:10 +00:00
timeout_toggled, NULL, NULL);
2010-01-03 05:42:10 +00:00
dbus_connection_set_dispatch_status_function(conn, dispatch_status,
NULL, NULL);
}
static gboolean setup_bus(DBusConnection *conn, const char *name,
DBusError *error)
{
gboolean result;
DBusDispatchStatus status;
if (name != NULL) {
result = g_dbus_request_name(conn, name, error);
if (error != NULL) {
if (dbus_error_is_set(error) == TRUE)
return FALSE;
}
if (result == FALSE)
return FALSE;
}
setup_dbus_with_main_loop(conn);
status = dbus_connection_get_dispatch_status(conn);
queue_dispatch(conn, status);
return TRUE;
}
DBusConnection *g_dbus_setup_bus(DBusBusType type, const char *name,
DBusError *error)
{
DBusConnection *conn;
conn = dbus_bus_get(type, error);
if (error != NULL) {
if (dbus_error_is_set(error) == TRUE)
return NULL;
}
if (conn == NULL)
return NULL;
2010-01-27 08:48:21 +00:00
if (setup_bus(conn, name, error) == FALSE) {
dbus_connection_unref(conn);
return NULL;
}
return conn;
}
DBusConnection *g_dbus_setup_private(DBusBusType type, const char *name,
DBusError *error)
{
DBusConnection *conn;
2010-01-03 05:42:10 +00:00
conn = dbus_bus_get_private(type, error);
if (error != NULL) {
if (dbus_error_is_set(error) == TRUE)
2010-01-03 05:42:10 +00:00
return NULL;
}
if (conn == NULL)
return NULL;
2010-01-27 08:48:21 +00:00
if (setup_bus(conn, name, error) == FALSE) {
dbus_connection_unref(conn);
return NULL;
}
return conn;
}
gboolean g_dbus_request_name(DBusConnection *connection, const char *name,
DBusError *error)
{
2010-01-03 05:42:10 +00:00
int result;
result = dbus_bus_request_name(connection, name,
DBUS_NAME_FLAG_DO_NOT_QUEUE, error);
if (error != NULL) {
if (dbus_error_is_set(error) == TRUE)
return FALSE;
}
if (result != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
if (error != NULL)
dbus_set_error(error, name, "Name already in use");
2010-01-03 05:42:10 +00:00
return FALSE;
}
2010-01-03 05:42:10 +00:00
return TRUE;
}
gboolean g_dbus_set_disconnect_function(DBusConnection *connection,
GDBusWatchFunction function,
void *user_data, DBusFreeFunction destroy)
{
struct disconnect_data *dc_data;
2010-01-03 05:42:10 +00:00
dc_data = g_new0(struct disconnect_data, 1);
2010-01-03 05:42:10 +00:00
dc_data->function = function;
dc_data->user_data = user_data;
dbus_connection_set_exit_on_disconnect(connection, FALSE);
if (g_dbus_add_signal_watch(connection, NULL, NULL,
DBUS_INTERFACE_LOCAL, "Disconnected",
2010-01-03 05:42:10 +00:00
disconnected_signal, dc_data, g_free) == 0) {
error("Failed to add watch for D-Bus Disconnected signal");
g_free(dc_data);
return FALSE;
}
return TRUE;
}