efl/src/lib/eldbus/eldbus_core.c

1610 lines
47 KiB
C
Raw Normal View History

2013-03-06 08:57:01 -08:00
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "eldbus_private.h"
#include "eldbus_private_types.h"
#define ELDBUS_CONNECTION_CHECK(conn) \
do \
{ \
EINA_SAFETY_ON_NULL_RETURN(conn); \
if (!EINA_MAGIC_CHECK(conn, ELDBUS_CONNECTION_MAGIC)) \
{ \
EINA_MAGIC_FAIL(conn, ELDBUS_CONNECTION_MAGIC); \
return; \
} \
EINA_SAFETY_ON_TRUE_RETURN(conn->refcount <= 0); \
} \
while (0)
#define ELDBUS_CONNECTION_CHECK_RETVAL(conn, retval) \
do \
{ \
EINA_SAFETY_ON_NULL_RETURN_VAL(conn, retval); \
if (!EINA_MAGIC_CHECK(conn, ELDBUS_CONNECTION_MAGIC)) \
{ \
EINA_MAGIC_FAIL(conn, ELDBUS_CONNECTION_MAGIC); \
return retval; \
} \
EINA_SAFETY_ON_TRUE_RETURN_VAL(conn->refcount <= 0, retval); \
} \
while (0)
typedef struct _Eldbus_Connection_Context_Event_Cb
{
EINA_INLIST;
Eldbus_Connection_Event_Cb cb;
const void *cb_data;
Eina_Bool deleted : 1;
} Eldbus_Connection_Context_Event_Cb;
typedef struct _Eldbus_Connection_Context_NOC_Cb
{
EINA_INLIST;
Eldbus_Name_Owner_Changed_Cb cb;
const void *cb_data;
Eina_Bool deleted : 1;
Ecore_Idle_Enterer *idle_enterer;
Eina_Bool allow_initial : 1;
} Eldbus_Connection_Context_NOC_Cb;
typedef struct _Eldbus_Handler_Data
{
EINA_INLIST;
int fd;
Ecore_Fd_Handler *fd_handler;
Eldbus_Connection *conn;
DBusWatch *watch;
int enabled;
} Eldbus_Handler_Data;
typedef struct _Eldbus_Timeout_Data
{
EINA_INLIST;
Ecore_Timer *handler;
DBusTimeout *timeout;
Eldbus_Connection *conn;
int interval;
} Eldbus_Timeout_Data;
static const Eldbus_Version _version = {VMAJ, VMIN, VMIC, VREV};
EAPI const Eldbus_Version * eldbus_version = &_version;
static int _eldbus_init_count = 0;
int _eldbus_log_dom = -1;
int eldbus_model_log_dom = -1;
/* We don't save ELDBUS_CONNECTION_TYPE_UNKNOWN in here so we need room for
* last - 1 elements */
static void *shared_connections[ELDBUS_CONNECTION_TYPE_LAST - 1];
static Eina_Hash *address_connections = NULL;
static void _eldbus_connection_event_callback_call(Eldbus_Connection *conn, Eldbus_Connection_Event_Type type, const void *event_info);
static void _eldbus_connection_context_event_cb_del(Eldbus_Connection_Context_Event *ce, Eldbus_Connection_Context_Event_Cb *ctx);
static void eldbus_dispatch_name_owner_change(Eldbus_Connection_Name *cn, const char *old_id);
static void _eldbus_connection_free(Eldbus_Connection *conn);
static void
eldbus_fd_handler_del(Eldbus_Handler_Data *hd)
{
if (!hd->fd_handler) return;
DBG("free Eldbus_Handler_Data %d", hd->fd);
hd->conn->fd_handlers = eina_inlist_remove(hd->conn->fd_handlers,
EINA_INLIST_GET(hd));
if (hd->fd_handler)
{
ecore_main_fd_handler_del(hd->fd_handler);
hd->fd_handler = NULL;
}
free(hd);
}
static void
_eldbus_fork_reset()
{
int i;
for (i =0; i < ELDBUS_CONNECTION_TYPE_LAST - 1; i++)
{
Eldbus_Connection *conn = shared_connections[i];
if (conn)
{
Eina_Inlist *list;
Eldbus_Handler_Data *fd_handler;
EINA_INLIST_FOREACH_SAFE(conn->fd_handlers, list, fd_handler)
dbus_watch_set_data(fd_handler->watch, NULL, NULL);
}
shared_connections[i] = NULL;
}
if (address_connections) eina_hash_free(address_connections);
address_connections = NULL;
}
EAPI int
eldbus_init(void)
{
if (_eldbus_init_count++ > 0)
return _eldbus_init_count;
if (!eina_init())
{
fputs("Eldbus: Unable to initialize eina\n", stderr);
return 0;
}
if (!ecore_init())
{
fputs("Eldbus: Unable to initialize ecore\n", stderr);
eina_shutdown();
return 0;
}
_eldbus_log_dom = eina_log_domain_register("eldbus", EINA_COLOR_BLUE);
if (_eldbus_log_dom < 0)
{
EINA_LOG_ERR("Unable to create an 'eldbus' log domain");
ecore_shutdown();
eina_shutdown();
return 0;
}
eldbus_model_log_dom = eina_log_domain_register("eldbus_model", EINA_COLOR_CYAN);
if (eldbus_model_log_dom < 0)
{
EINA_LOG_ERR("Unable to create an 'eldbus_model' log domain");
eina_log_domain_unregister(_eldbus_log_dom);
_eldbus_log_dom = -1;
ecore_shutdown();
eina_shutdown();
return 0;
}
eina_magic_string_set(ELDBUS_CONNECTION_MAGIC, "Eldbus_Connection");
eina_magic_string_set(ELDBUS_MESSAGE_MAGIC, "Eldbus_Message");
eina_magic_string_set(ELDBUS_SIGNAL_HANDLER_MAGIC, "Eldbus_Signal_Handler");
eina_magic_string_set(ELDBUS_PENDING_MAGIC, "Eldbus_Pending");
eina_magic_string_set(ELDBUS_OBJECT_MAGIC, "Eldbus_Object");
eina_magic_string_set(ELDBUS_PROXY_MAGIC, "Eldbus_Proxy");
eina_magic_string_set(ELDBUS_MESSAGE_ITERATOR_MAGIC, "Eldbus_Message_Iterator");
eina_magic_string_set(ELDBUS_SERVICE_INTERFACE_MAGIC, "Eldbus_Service_Interface");
if (!eldbus_message_init()) goto message_failed;
if (!eldbus_signal_handler_init()) goto signal_handler_failed;
if (!eldbus_pending_init()) goto pending_failed;
if (!eldbus_object_init()) goto object_failed;
if (!eldbus_proxy_init()) goto proxy_failed;
if (!eldbus_service_init()) goto service_failed;
ecore_fork_reset_callback_add(_eldbus_fork_reset, NULL);
return _eldbus_init_count;
service_failed:
eldbus_proxy_shutdown();
proxy_failed:
eldbus_object_shutdown();
object_failed:
eldbus_pending_shutdown();
pending_failed:
eldbus_signal_handler_shutdown();
signal_handler_failed:
eldbus_message_shutdown();
message_failed:
eina_log_domain_unregister(eldbus_model_log_dom);
eldbus_model_log_dom = -1;
eina_log_domain_unregister(_eldbus_log_dom);
_eldbus_log_dom = -1;
ecore_shutdown();
eina_shutdown();
return 0;
}
static void
print_live_connection(Eldbus_Connection *conn)
{
if (!conn->names)
ERR("conn=%p has no alive objects", conn);
else
{
Eina_Iterator *iter = eina_hash_iterator_data_new(conn->names);
Eldbus_Connection_Name *name;
EINA_ITERATOR_FOREACH(iter, name)
{
Eldbus_Object *obj;
Eina_Iterator *inner_itr;
if (!name->objects) continue;
inner_itr = eina_hash_iterator_data_new(name->objects);
EINA_ITERATOR_FOREACH(inner_itr, obj)
ERR("conn=%p alive object=%p %s of bus=%s", conn, obj,
obj->name, name->name);
eina_iterator_free(inner_itr);
}
eina_iterator_free(iter);
}
if (!conn->pendings)
ERR("conn=%p has no alive pending calls", conn);
else
{
Eldbus_Pending *p;
EINA_INLIST_FOREACH(conn->pendings, p)
ERR("conn=%p alive pending call=%p dest=%s path=%s %s.%s()",
conn, p,
eldbus_pending_destination_get(p),
eldbus_pending_path_get(p),
eldbus_pending_interface_get(p),
eldbus_pending_method_get(p));
}
}
EAPI int
eldbus_shutdown(void)
{
if (_eldbus_init_count <= 0)
{
ERR("Init count not greater than 0 in shutdown.");
_eldbus_init_count = 0;
return 0;
}
if (--_eldbus_init_count)
return _eldbus_init_count;
ecore_fork_reset_callback_del(_eldbus_fork_reset, NULL);
if (shared_connections[ELDBUS_CONNECTION_TYPE_SESSION - 1])
{
CRI("Alive TYPE_SESSION connection");
print_live_connection(shared_connections[ELDBUS_CONNECTION_TYPE_SESSION - 1]);
}
if (shared_connections[ELDBUS_CONNECTION_TYPE_SYSTEM - 1])
{
CRI("Alive TYPE_SYSTEM connection");
print_live_connection(shared_connections[ELDBUS_CONNECTION_TYPE_SYSTEM - 1]);
}
if (shared_connections[ELDBUS_CONNECTION_TYPE_STARTER - 1])
{
CRI("Alive TYPE_STARTER connection");
print_live_connection(shared_connections[ELDBUS_CONNECTION_TYPE_STARTER - 1]);
}
if (shared_connections[ELDBUS_CONNECTION_TYPE_ADDRESS - 1])
{
if (eina_hash_population(address_connections))
{
Eina_Iterator *it;
Eina_Hash_Tuple *tuple;
it = eina_hash_iterator_tuple_new(address_connections);
EINA_ITERATOR_FOREACH(it, tuple)
{
CRI("Alive TYPE_ADDRESS connection: %s", (char*)tuple->key);
print_live_connection(tuple->data);
}
eina_iterator_free(it);
}
eina_hash_free(address_connections);
address_connections = shared_connections[ELDBUS_CONNECTION_TYPE_ADDRESS - 1] = NULL;
}
eldbus_service_shutdown();
eldbus_proxy_shutdown();
eldbus_object_shutdown();
eldbus_pending_shutdown();
eldbus_signal_handler_shutdown();
eldbus_message_shutdown();
ecore_shutdown();
eina_log_domain_unregister(eldbus_model_log_dom);
eldbus_model_log_dom = -1;
eina_log_domain_unregister(_eldbus_log_dom);
_eldbus_log_dom = -1;
eina_shutdown();
return 0;
}
/* TODO: mempool of Eldbus_Context_Free_Cb */
typedef struct _Eldbus_Context_Free_Cb
{
EINA_INLIST;
Eldbus_Free_Cb cb;
const void *data;
} Eldbus_Context_Free_Cb;
void
eldbus_cbs_free_dispatch(Eina_Inlist **p_lst, const void *dead_pointer)
{
Eina_Inlist *lst = *p_lst;
*p_lst = NULL;
while (lst)
{
Eina_Inlist *next = lst->next;
Eldbus_Context_Free_Cb *ctx;
ctx = EINA_INLIST_CONTAINER_GET(lst, Eldbus_Context_Free_Cb);
ctx->cb((void *)ctx->data, dead_pointer);
free(ctx);
lst = next;
}
}
Eina_Inlist *
eldbus_cbs_free_add(Eina_Inlist *lst, Eldbus_Free_Cb cb, const void *data)
{
Eldbus_Context_Free_Cb *ctx = malloc(sizeof(Eldbus_Context_Free_Cb));
EINA_SAFETY_ON_NULL_RETURN_VAL(ctx, lst);
ctx->cb = cb;
ctx->data = data;
return eina_inlist_append(lst, EINA_INLIST_GET(ctx));
}
Eina_Inlist *
eldbus_cbs_free_del(Eina_Inlist *lst, Eldbus_Free_Cb cb, const void *data)
{
Eldbus_Context_Free_Cb *ctx;
EINA_INLIST_FOREACH(lst, ctx)
{
if (ctx->cb != cb) continue;
if ((data) && (ctx->data != data)) continue;
lst = eina_inlist_remove(lst, EINA_INLIST_GET(ctx));
free(ctx);
return lst;
}
ERR("Couldn't find cb_free=%p data=%p", cb, data);
return lst;
}
typedef struct _Eldbus_Data
{
EINA_INLIST;
const void *data;
unsigned int keylen;
char key[];
} Eldbus_Data;
static inline Eldbus_Data *
eldbus_data_find(Eina_Inlist **p_lst, const char *key)
{
unsigned int keylen = strlen(key);
Eldbus_Data *d;
EINA_INLIST_FOREACH(*p_lst, d)
{
if ((keylen == d->keylen) && (memcmp(key, d->key, keylen) == 0))
{
*p_lst = eina_inlist_promote(*p_lst, EINA_INLIST_GET(d));
return d;
}
}
return NULL;
}
void
eldbus_data_set(Eina_Inlist **p_lst, const char *key, const void *data)
{
Eldbus_Data *d = eldbus_data_find(p_lst, key);
unsigned int keylen = strlen(key);
if (d)
{
*p_lst = eina_inlist_remove(*p_lst, EINA_INLIST_GET(d));
free(d);
}
d = malloc(sizeof(Eldbus_Data) + keylen + 1);
EINA_SAFETY_ON_NULL_RETURN(d);
d->data = data;
d->keylen = keylen;
memcpy(d->key, key, keylen + 1);
*p_lst = eina_inlist_prepend(*p_lst, EINA_INLIST_GET(d));
}
void *
eldbus_data_get(Eina_Inlist **p_lst, const char *key)
{
Eldbus_Data *d = eldbus_data_find(p_lst, key);
return d ? (void *)d->data : NULL;
}
void *
eldbus_data_del(Eina_Inlist **p_lst, const char *key)
{
Eldbus_Data *d = eldbus_data_find(p_lst, key);
void *ret;
if (!d) return NULL;
ret = (void *)d->data;
*p_lst = eina_inlist_remove(*p_lst, EINA_INLIST_GET(d));
free(d);
return ret;
}
void
eldbus_data_del_all(Eina_Inlist **p_list)
{
Eina_Inlist *n = *p_list;
*p_list = NULL;
while (n)
{
Eldbus_Data *d = EINA_INLIST_CONTAINER_GET(n, Eldbus_Data);
n = eina_inlist_remove(n, n);
DBG("key=%s, data=%p", d->key, d->data);
free(d);
}
}
static void
eldbus_connection_name_gc(Eldbus_Connection *conn, Eldbus_Connection_Name *cn)
{
if (cn->refcount > 0)
return;
if (cn->objects && eina_hash_population(cn->objects) > 0)
return;
if (cn->event_handlers.list != NULL)
return;
eina_hash_del(conn->names, cn->name, cn);
if (cn->name_owner_changed)
eldbus_signal_handler_del(cn->name_owner_changed);
if (cn->objects)
eina_hash_free(cn->objects);
eina_stringshare_del(cn->name);
if (cn->name_owner_get)
eldbus_pending_cancel(cn->name_owner_get);
if (cn->unique_id)
eina_stringshare_del(cn->unique_id);
eina_list_free(cn->event_handlers.to_delete);
free(cn);
}
void
eldbus_connection_name_object_del(Eldbus_Connection *conn, const Eldbus_Object *obj)
{
Eldbus_Connection_Name *cn = eina_hash_find(conn->names, obj->name);
if (!cn) return;
if (!cn->objects) return;
eina_hash_del(cn->objects, obj->path, obj);
eldbus_connection_name_gc(conn, cn);
}
void
eldbus_connection_name_object_set(Eldbus_Connection *conn, Eldbus_Object *obj)
{
Eldbus_Connection_Name *cn;
cn = eldbus_connection_name_get(conn, obj->name);
eina_hash_add(cn->objects, obj->path, obj);
return;
}
static void
on_name_owner_changed(void *data, const Eldbus_Message *msg)
{
const char *bus, *older_id, *new_id;
Eldbus_Connection_Name *cn = data;
if (!eldbus_message_arguments_get(msg, "sss", &bus, &older_id, &new_id))
{
ERR("Error getting arguments from NameOwnerChanged cn=%s", cn->name);
return;
}
eina_stringshare_replace(&cn->unique_id, new_id);
eldbus_dispatch_name_owner_change(cn, older_id);
}
static void
on_get_name_owner(void *data, const Eldbus_Message *msg, Eldbus_Pending *pending EINA_UNUSED)
{
const char *unique_id = "", *error_name;
Eldbus_Connection_Name *cn = data;
cn->name_owner_get = NULL;
if (eldbus_message_error_get(msg, &error_name, NULL))
{
if (!strcmp(error_name, ELDBUS_ERROR_PENDING_CANCELED))
return;
DBG("GetNameOwner of bus = %s returned an error", cn->name);
}
else if (!eldbus_message_arguments_get(msg, "s", &unique_id))
ERR("Error getting arguments from GetNameOwner");
cn->unique_id = eina_stringshare_add(unique_id);
eldbus_dispatch_name_owner_change(cn, NULL);
}
void
eldbus_connection_name_ref(Eldbus_Connection_Name *cn)
{
EINA_SAFETY_ON_NULL_RETURN(cn);
cn->refcount++;
}
void
eldbus_connection_name_unref(Eldbus_Connection *conn, Eldbus_Connection_Name *cn)
{
ELDBUS_CONNECTION_CHECK(conn);
EINA_SAFETY_ON_NULL_RETURN(cn);
cn->refcount--;
eldbus_connection_name_gc(conn, cn);
}
Eldbus_Connection_Name *
eldbus_connection_name_get(Eldbus_Connection *conn, const char *name)
{
Eldbus_Connection_Name *cn;
ELDBUS_CONNECTION_CHECK_RETVAL(conn, NULL);
cn = eina_hash_find(conn->names, name);
if (cn) return cn;
cn = calloc(1, sizeof(Eldbus_Connection_Name));
EINA_SAFETY_ON_NULL_RETURN_VAL(cn, NULL);
cn->name = eina_stringshare_add(name);
cn->objects = eina_hash_string_superfast_new(NULL);
if (!strcmp(name, ELDBUS_FDO_BUS))
{
cn->unique_id = eina_stringshare_add(name);
goto end;
}
if (name[0] == ':')
cn->unique_id = eina_stringshare_add(name);
else
cn->name_owner_get = eldbus_name_owner_get(conn, cn->name, on_get_name_owner, cn);
cn->name_owner_changed = _eldbus_signal_handler_add(conn, ELDBUS_FDO_BUS,
ELDBUS_FDO_PATH,
ELDBUS_FDO_INTERFACE,
edbus: Fix bug found in refactored banshee example Refactor edbus_signal_handler_add() so internal signal handlers don't set the connection free callback. This fixes the bug in which EDBus_Connection was freeing the signal handler of EDBus_Conenction_Name ==22814== Invalid read of size 4 ==22814== at 0x40564B0: edbus_signal_handler_del (edbus_signal_handler.c:278) ==22814== by 0x4040E65: _edbus_connection_name_unref (edbus_core.c:507) ==22814== by 0x404106B: edbus_connection_name_owner_monitor (edbus_core.c:520) ==22814== by 0x4055F63: _edbus_signal_handler_clean (edbus_signal_handler.c:217) ==22814== by 0x40564F8: edbus_signal_handler_del (edbus_signal_handler.c:279) ==22814== by 0x4043088: _edbus_connection_unref (edbus_core.c:1045) ==22814== by 0x404352F: edbus_connection_unref (edbus_core.c:1105) ==22814== by 0x80498AA: main (banshee.c:233) ==22814== Address 0x44bea48 is 0 bytes inside a block of size 72 free'd ==22814== at 0x402C06C: free (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so) ==22814== by 0x4056118: _edbus_signal_handler_del (edbus_signal_handler.c:249) ==22814== by 0x4056401: edbus_signal_handler_unref (edbus_signal_handler.c:272) ==22814== by 0x4056503: edbus_signal_handler_del (edbus_signal_handler.c:280) ==22814== by 0x4043088: _edbus_connection_unref (edbus_core.c:1045) ==22814== by 0x404352F: edbus_connection_unref (edbus_core.c:1105) ==22814== by 0x80498AA: main (banshee.c:233) ==22814== CRI<22814>: src/lib/edbus_signal_handler.c:278 edbus_signal_handler_del() *** Eina Magic Check Failed !!! Input handle has already been freed! *** NAUGHTY PROGRAMMER!!! *** SPANK SPANK SPANK!!! *** Now go fix your code. Tut tut tut! Patch by: José Roberto de Souza <zehortigoza@profusion.mobi> SVN revision: 80686
2012-12-11 11:50:01 -08:00
"NameOwnerChanged",
on_name_owner_changed, cn);
eldbus_signal_handler_match_extra_set(cn->name_owner_changed, "arg0",
cn->name, NULL);
end:
eina_hash_direct_add(conn->names, cn->name, cn);
return cn;
}
Eldbus_Object *
eldbus_connection_name_object_get(Eldbus_Connection *conn, const char *name, const char *path)
{
Eldbus_Connection_Name *cn;
ELDBUS_CONNECTION_CHECK_RETVAL(conn, NULL);
cn = eina_hash_find(conn->names, name);
if (!cn) return NULL;
if (!cn->objects) return NULL;
return eina_hash_find(cn->objects, path);
}
static Eina_Bool
eldbus_fd_handler(void *data, Ecore_Fd_Handler *fd_handler)
{
Eldbus_Handler_Data *hd = data;
unsigned int condition = 0;
DBG("Got Ecore_Fd_Handle@%p", fd_handler);
if (ecore_main_fd_handler_active_get(fd_handler, ECORE_FD_READ)) condition |= DBUS_WATCH_READABLE;
if (ecore_main_fd_handler_active_get(fd_handler, ECORE_FD_WRITE)) condition |= DBUS_WATCH_WRITABLE;
if (ecore_main_fd_handler_active_get(fd_handler, ECORE_FD_ERROR)) condition |= DBUS_WATCH_ERROR;
DBG("dbus connection@%p fdh=%d flags: [%s%s%s]", hd->conn, hd->fd,
(condition & DBUS_WATCH_READABLE) ? "read " : "",
(condition & DBUS_WATCH_WRITABLE) ? "write " : "",
(condition & DBUS_WATCH_ERROR) ? "error" : "");
dbus_watch_handle(hd->watch, condition);
return ECORE_CALLBACK_RENEW;
}
static void
eldbus_fd_handler_add(Eldbus_Handler_Data *hd)
{
unsigned int dflags;
Ecore_Fd_Handler_Flags eflags;
if (hd->fd_handler) return;
dflags = dbus_watch_get_flags(hd->watch);
eflags = ECORE_FD_ERROR;
if (dflags & DBUS_WATCH_READABLE) eflags |= ECORE_FD_READ;
if (dflags & DBUS_WATCH_WRITABLE) eflags |= ECORE_FD_WRITE;
DBG("Watching fd %d with flags: [%s%serror]", hd->fd,
(eflags & ECORE_FD_READ) ? "read " : "",
(eflags & ECORE_FD_WRITE) ? "write " : "");
hd->fd_handler = ecore_main_fd_handler_add(hd->fd,
eflags,
eldbus_fd_handler,
hd,
NULL,
NULL);
}
static void
eldbus_handler_data_free(void *data)
{
Eldbus_Handler_Data *hd = data;
eldbus_fd_handler_del(hd);
}
static dbus_bool_t
cb_watch_add(DBusWatch *watch, void *data)
{
Eldbus_Connection *conn = data;
Eldbus_Handler_Data *hd;
if (!dbus_watch_get_enabled(watch)) return EINA_TRUE;
DBG("cb_watch_add (enabled: %d)", dbus_watch_get_unix_fd(watch));
hd = calloc(1, sizeof(Eldbus_Handler_Data));
EINA_SAFETY_ON_NULL_RETURN_VAL(hd, EINA_FALSE);
dbus_watch_set_data(watch, hd, eldbus_handler_data_free);
hd->conn = conn;
hd->watch = watch;
hd->enabled = dbus_watch_get_enabled(watch);
hd->fd = dbus_watch_get_unix_fd(hd->watch);
conn->fd_handlers = eina_inlist_append(hd->conn->fd_handlers,
EINA_INLIST_GET(hd));
eldbus_fd_handler_add(hd);
return EINA_TRUE;
}
static void
cb_watch_del(DBusWatch *watch, void *data EINA_UNUSED)
{
DBG("cb_watch_del");
/* will trigger eldbus_handler_data_free() */
dbus_watch_set_data(watch, NULL, NULL);
}
static void
cb_watch_toggle(DBusWatch *watch, void *data EINA_UNUSED)
{
Eldbus_Handler_Data *hd;
hd = dbus_watch_get_data(watch);
if (!hd) return;
DBG("cb_watch_toggle %d", hd->fd);
hd->enabled = dbus_watch_get_enabled(watch);
DBG("watch %p is %sabled", hd, hd->enabled ? "en" : "dis");
if (hd->enabled)
{
eldbus_fd_handler_add(hd);
}
else
{
ecore_main_fd_handler_del(hd->fd_handler);
hd->fd_handler = NULL;
}
}
static void
eldbus_timeout_data_free(void *timeout_data)
{
Eldbus_Timeout_Data *td = timeout_data;
td->conn->timeouts = eina_inlist_remove(td->conn->timeouts,
EINA_INLIST_GET(td));
DBG("Timeout -- freeing timeout_data %p", td);
if (td->handler) ecore_timer_del(td->handler);
free(td);
}
static Eina_Bool
eldbus_timeout_handler(void *data)
{
Eldbus_Timeout_Data *td = data;
td->handler = NULL;
if (!dbus_timeout_get_enabled(td->timeout))
{
DBG("timeout_handler (not enabled, ending)");
return ECORE_CALLBACK_CANCEL;
}
DBG("Telling dbus to handle timeout with data %p", data);
dbus_timeout_handle(td->timeout);
return ECORE_CALLBACK_CANCEL;
}
static dbus_bool_t
cb_timeout_add(DBusTimeout *timeout, void *data)
{
Eldbus_Connection *conn = data;
Eldbus_Timeout_Data *td;
if (!dbus_timeout_get_enabled(timeout))
return EINA_TRUE;
DBG("Adding timeout for connection@%p", conn);
td = calloc(1, sizeof(Eldbus_Timeout_Data));
EINA_SAFETY_ON_NULL_RETURN_VAL(td, EINA_FALSE);
td->conn = conn;
dbus_timeout_set_data(timeout, (void *)td, eldbus_timeout_data_free);
td->interval = dbus_timeout_get_interval(timeout);
td->timeout = timeout;
td->handler = ecore_timer_add(td->interval / 1000.0, eldbus_timeout_handler, td);
conn->timeouts = eina_inlist_append(conn->timeouts,
EINA_INLIST_GET(td));
return EINA_TRUE;
}
static void
cb_timeout_del(DBusTimeout *timeout, void *data EINA_UNUSED)
{
DBG("timeout del!");
/* will trigger eldbus_timeout_data_free() */
dbus_timeout_set_data(timeout, NULL, NULL);
}
static void
cb_timeout_toggle(DBusTimeout *timeout, void *data EINA_UNUSED)
{
Eldbus_Timeout_Data *td;
td = dbus_timeout_get_data(timeout);
DBG("Timeout toggle; data@%p", td);
if (dbus_timeout_get_enabled(td->timeout))
{
td->interval = dbus_timeout_get_interval(timeout);
td->handler
= ecore_timer_add(td->interval, eldbus_timeout_handler, td);
DBG("Timeout is enabled with interval %d, timer@%p",
td->interval, td->handler);
}
else
{
DBG("Timeout is disabled, destroying timer@%p", td->handler);
ecore_timer_del(td->handler);
td->handler = NULL;
}
}
static Eina_Bool
eldbus_idle_enterer(void *data)
{
Eldbus_Connection *conn = data;
DBG("Connection@%p: Dispatch status: %d", conn,
dbus_connection_get_dispatch_status(conn->dbus_conn));
if (dbus_connection_get_dispatch_status(conn->dbus_conn) ==
DBUS_DISPATCH_COMPLETE)
{
DBG("Connection@%p: Dispatch complete, idle_enterer@%p finishing",
conn, conn->idle_enterer);
conn->idle_enterer = NULL;
return ECORE_CALLBACK_CANCEL;
}
DBG("Connection@%p: Dispatching", conn);
eldbus: Fix crash when removing the last reference of the message container inside of the message callback If user try to remove the last reference of proxy, object, connection or eldbus(lib) inside of message callback it was causing the eldbus_pending_dispatch() being called 2 times, one because of the eldbus_cancel() that is triggered when the last reference of the message parent is removed and another after the return of the user callback. ==6545== Invalid read of size 8 ==6545== at 0x52F784E: eldbus_cbs_free_dispatch (eldbus_core.c:266) ==6545== by 0x53064AA: eldbus_pending_dispatch (eldbus_pending.c:227) ==6545== by 0x5305961: cb_pending (eldbus_pending.c:74) ==6545== by 0x6B29DB1: ??? (in /usr/lib/libdbus-1.so.3.8.9) ==6545== by 0x6B2D280: dbus_connection_dispatch (in /usr/lib/libdbus-1.so.3.8.9) ==6545== by 0x52F93B4: eldbus_idler (eldbus_core.c:773) ==6545== by 0x4E4B300: _ecore_call_task_cb (ecore_private.h:305) ==6545== by 0x4E4B78F: _ecore_idler_all_call (ecore_idler.c:143) ==6545== by 0x4E4EA73: _ecore_main_loop_spin_core (ecore_main.c:1768) ==6545== by 0x4E4EAF1: _ecore_main_loop_spin_timers (ecore_main.c:1802) ==6545== by 0x4E4ED01: _ecore_main_loop_iterate_internal (ecore_main.c:1925) ==6545== by 0x4E4D03B: ecore_main_loop_begin (ecore_main.c:983) ==6545== Address 0x701aa78 is 104 bytes inside a block of size 128 free'd ==6545== at 0x4C2B200: free (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) ==6545== by 0x530655B: eldbus_pending_dispatch (eldbus_pending.c:241) ==6545== by 0x5306763: eldbus_pending_cancel (eldbus_pending.c:259) ==6545== by 0x52F29DB: _eldbus_proxy_clear (eldbus_proxy.c:146) ==6545== by 0x52F3057: _eldbus_proxy_unref (eldbus_proxy.c:244) ==6545== by 0x52F3393: eldbus_proxy_unref (eldbus_proxy.c:264) ==6545== by 0x401039: on_get_playlists (banshee.c:53) ==6545== by 0x5306493: eldbus_pending_dispatch (eldbus_pending.c:225) ==6545== by 0x5305961: cb_pending (eldbus_pending.c:74) ==6545== by 0x6B29DB1: ??? (in /usr/lib/libdbus-1.so.3.8.9) ==6545== by 0x6B2D280: dbus_connection_dispatch (in /usr/lib/libdbus-1.so.3.8.9) ==6545== by 0x52F93B4: eldbus_idler (eldbus_core.c:773) Now we will remove the pending from parent pending list before call the user callback, this way only the pending messages will be canceled. Also we need increase the eldbus reference before call dbus_connection_dispatch() or user could remove the last reference of eldbus inside of a message callback when we still are holding one reference of the connection. @fix ref T1908
2014-12-17 10:57:03 -08:00
eldbus_init();
eldbus_connection_ref(conn);
DBusDispatchStatus status;
do
{
status = dbus_connection_dispatch(conn->dbus_conn);
}
while(status == DBUS_DISPATCH_DATA_REMAINS);
eldbus_connection_unref(conn);
eldbus: Fix crash when removing the last reference of the message container inside of the message callback If user try to remove the last reference of proxy, object, connection or eldbus(lib) inside of message callback it was causing the eldbus_pending_dispatch() being called 2 times, one because of the eldbus_cancel() that is triggered when the last reference of the message parent is removed and another after the return of the user callback. ==6545== Invalid read of size 8 ==6545== at 0x52F784E: eldbus_cbs_free_dispatch (eldbus_core.c:266) ==6545== by 0x53064AA: eldbus_pending_dispatch (eldbus_pending.c:227) ==6545== by 0x5305961: cb_pending (eldbus_pending.c:74) ==6545== by 0x6B29DB1: ??? (in /usr/lib/libdbus-1.so.3.8.9) ==6545== by 0x6B2D280: dbus_connection_dispatch (in /usr/lib/libdbus-1.so.3.8.9) ==6545== by 0x52F93B4: eldbus_idler (eldbus_core.c:773) ==6545== by 0x4E4B300: _ecore_call_task_cb (ecore_private.h:305) ==6545== by 0x4E4B78F: _ecore_idler_all_call (ecore_idler.c:143) ==6545== by 0x4E4EA73: _ecore_main_loop_spin_core (ecore_main.c:1768) ==6545== by 0x4E4EAF1: _ecore_main_loop_spin_timers (ecore_main.c:1802) ==6545== by 0x4E4ED01: _ecore_main_loop_iterate_internal (ecore_main.c:1925) ==6545== by 0x4E4D03B: ecore_main_loop_begin (ecore_main.c:983) ==6545== Address 0x701aa78 is 104 bytes inside a block of size 128 free'd ==6545== at 0x4C2B200: free (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) ==6545== by 0x530655B: eldbus_pending_dispatch (eldbus_pending.c:241) ==6545== by 0x5306763: eldbus_pending_cancel (eldbus_pending.c:259) ==6545== by 0x52F29DB: _eldbus_proxy_clear (eldbus_proxy.c:146) ==6545== by 0x52F3057: _eldbus_proxy_unref (eldbus_proxy.c:244) ==6545== by 0x52F3393: eldbus_proxy_unref (eldbus_proxy.c:264) ==6545== by 0x401039: on_get_playlists (banshee.c:53) ==6545== by 0x5306493: eldbus_pending_dispatch (eldbus_pending.c:225) ==6545== by 0x5305961: cb_pending (eldbus_pending.c:74) ==6545== by 0x6B29DB1: ??? (in /usr/lib/libdbus-1.so.3.8.9) ==6545== by 0x6B2D280: dbus_connection_dispatch (in /usr/lib/libdbus-1.so.3.8.9) ==6545== by 0x52F93B4: eldbus_idler (eldbus_core.c:773) Now we will remove the pending from parent pending list before call the user callback, this way only the pending messages will be canceled. Also we need increase the eldbus reference before call dbus_connection_dispatch() or user could remove the last reference of eldbus inside of a message callback when we still are holding one reference of the connection. @fix ref T1908
2014-12-17 10:57:03 -08:00
eldbus_shutdown();
return ECORE_CALLBACK_RENEW;
}
static void
cb_dispatch_status(DBusConnection *dbus_conn EINA_UNUSED, DBusDispatchStatus new_status, void *data)
{
Eldbus_Connection *conn = data;
if (!conn->refcount)
{
DBG("Connection[%p] being freed, dispatch blocked", conn);
return;
}
DBG("Connection@%p: Dispatch status: %d", conn, new_status);
if ((new_status == DBUS_DISPATCH_DATA_REMAINS) && (!conn->idle_enterer))
{
conn->idle_enterer = ecore_idle_enterer_add(eldbus_idle_enterer, conn);
DBG("Connection@%p: Adding idle_enterer@%p to handle remaining dispatch data",
conn, conn->idle_enterer);
}
else if ((new_status != DBUS_DISPATCH_DATA_REMAINS) && (conn->idle_enterer))
{
DBG("Connection@%p: No remaining dispatch data, clearing idle_enterer@%p",
conn, conn->idle_enterer);
ecore_idle_enterer_del(conn->idle_enterer);
conn->idle_enterer = NULL;
}
}
static inline Eina_Bool
extra_arguments_check(DBusMessage *msg, Eldbus_Signal_Handler *sh)
{
DBusMessageIter iter;
Signal_Argument *arg;
unsigned int arg_index = 0;
dbus_message_iter_init(msg, &iter);
EINA_INLIST_FOREACH(sh->args, arg)
{
const char *arg_msg;
int type = 0;
while((arg->index > arg_index) && dbus_message_iter_next(&iter))
arg_index++;
if (arg_index != arg->index)
return EINA_FALSE;
type = dbus_message_iter_get_arg_type(&iter);
if (!(type == 's' || type == 'o'))
return EINA_FALSE;
dbus_message_iter_get_basic(&iter, &arg_msg);
if (strcmp(arg_msg, arg->value))
return EINA_FALSE;
}
return EINA_TRUE;
}
static void
cb_signal_dispatcher(Eldbus_Connection *conn, DBusMessage *msg)
{
Eldbus_Message *eldbus_msg;
Eina_Inlist *next;
eldbus_msg = eldbus_message_new(EINA_FALSE);
EINA_SAFETY_ON_NULL_RETURN(eldbus_msg);
eldbus_msg->dbus_msg = dbus_message_ref(msg);
dbus_message_iter_init(eldbus_msg->dbus_msg,
&eldbus_msg->iterator->dbus_iterator);
eldbus_connection_ref(conn);
eldbus_init();
/*
* Do the walking open-coded so we don't crash if a callback
* removes other signal handlers from the list and we don't own
* yet a reference to them.
*/
next = conn->signal_handlers;
while (next != NULL)
{
Eldbus_Signal_Handler *sh;
sh = EINA_INLIST_CONTAINER_GET(next, Eldbus_Signal_Handler);
next = next->next;
if (sh->dangling) continue;
if (sh->sender)
{
if (sh->bus)
{
if ((sh->bus->unique_id == NULL) ||
(!sh->bus->unique_id[0]) ||
(!dbus_message_has_sender(msg, sh->bus->unique_id)))
continue;
}
else
if (!dbus_message_has_sender(msg, sh->sender)) continue;
}
if (sh->path && !dbus_message_has_path(msg, sh->path)) continue;
if (sh->interface && !dbus_message_has_interface(msg, sh->interface))
continue;
if (sh->member && !dbus_message_has_member(msg, sh->member)) continue;
if (!extra_arguments_check(msg, sh)) continue;
eldbus_signal_handler_ref(sh);
sh->cb((void *)sh->cb_data, eldbus_msg);
/* update next signal handler because the list may have changed */
next = EINA_INLIST_GET(sh)->next;
eldbus_signal_handler_unref(sh);
/*
* Rewind iterator so another signal handler matching the same signal
* can iterate over it.
*/
dbus_message_iter_init(eldbus_msg->dbus_msg,
&eldbus_msg->iterator->dbus_iterator);
}
eldbus_message_unref(eldbus_msg);
eldbus_connection_unref(conn);
eldbus_shutdown();
}
static DBusHandlerResult
eldbus_filter(DBusConnection *conn_dbus EINA_UNUSED, DBusMessage *message, void *user_data)
{
Eldbus_Connection *conn = user_data;
DBG("Connection@%p Got message:\n"
" Type: %s\n"
" Path: %s\n"
" Interface: %s\n"
" Member: %s\n"
" Sender: %s", conn,
dbus_message_type_to_string(dbus_message_get_type(message)),
dbus_message_get_path(message),
dbus_message_get_interface(message),
dbus_message_get_member(message),
dbus_message_get_sender(message));
switch (dbus_message_get_type(message))
{
case DBUS_MESSAGE_TYPE_METHOD_CALL:
DBG(" Signature: %s", dbus_message_get_signature(message));
break;
case DBUS_MESSAGE_TYPE_METHOD_RETURN:
case DBUS_MESSAGE_TYPE_ERROR:
DBG(" Reply serial: %d", dbus_message_get_reply_serial(message));
break;
case DBUS_MESSAGE_TYPE_SIGNAL:
DBG(" Signature: %s", dbus_message_get_signature(message));
cb_signal_dispatcher(conn, message);
break;
}
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
static void
eldbus_connection_setup(Eldbus_Connection *conn)
{
DBG("Setting up connection %p", conn);
/* connection_setup */
dbus_connection_set_exit_on_disconnect(conn->dbus_conn, EINA_FALSE);
dbus_connection_set_watch_functions(conn->dbus_conn,
cb_watch_add,
cb_watch_del,
cb_watch_toggle,
conn,
NULL);
dbus_connection_set_timeout_functions(conn->dbus_conn,
cb_timeout_add,
cb_timeout_del,
cb_timeout_toggle,
conn,
NULL);
dbus_connection_set_dispatch_status_function(conn->dbus_conn,
cb_dispatch_status,
conn, NULL);
dbus_connection_add_filter(conn->dbus_conn, eldbus_filter, conn, NULL);
cb_dispatch_status(conn->dbus_conn,
dbus_connection_get_dispatch_status(conn->dbus_conn),
conn);
}
static void
_disconnected(void *data, const Eldbus_Message *msg EINA_UNUSED)
{
Eldbus_Connection *conn = data;
Ecore_Event_Signal_Exit *ev;
_eldbus_connection_event_callback_call(
conn, ELDBUS_CONNECTION_EVENT_DISCONNECTED, NULL);
if (conn->type != ELDBUS_CONNECTION_TYPE_SESSION) return;
ev = calloc(1, sizeof(Ecore_Event_Signal_Exit));
if (!ev) return;
ev->quit = EINA_TRUE;
ecore_event_add(ECORE_EVENT_SIGNAL_EXIT, ev, NULL, NULL);
}
/* Param address is only used for ELDBUS_CONNECTION_TYPE_ADDRESS type */
static Eldbus_Connection *
_connection_get(Eldbus_Connection_Type type, const char *address, Eina_Bool shared)
{
Eldbus_Connection *conn;
DBusError err;
Eldbus_Object *obj;
EINA_SAFETY_ON_FALSE_RETURN_VAL((type < ELDBUS_CONNECTION_TYPE_LAST) &&
(type > ELDBUS_CONNECTION_TYPE_UNKNOWN), NULL);
EINA_SAFETY_ON_TRUE_RETURN_VAL((type == ELDBUS_CONNECTION_TYPE_ADDRESS) &&
(address == NULL), NULL);
conn = calloc(1, sizeof(Eldbus_Connection));
EINA_SAFETY_ON_NULL_RETURN_VAL(conn, NULL);
dbus_error_init(&err);
if (type == ELDBUS_CONNECTION_TYPE_ADDRESS)
conn->dbus_conn = dbus_connection_open_private(address, &err);
else
conn->dbus_conn = dbus_bus_get_private(type - 1, &err);
if (!conn->dbus_conn || dbus_error_is_set(&err))
{
free(conn);
ERR("Error connecting to bus of type %d. error name: %s error message: %s",
type, err.name, err.message);
return NULL;
}
if (type == ELDBUS_CONNECTION_TYPE_ADDRESS &&
!dbus_bus_register(conn->dbus_conn, &err))
{
dbus_connection_close(conn->dbus_conn);
dbus_connection_unref(conn->dbus_conn);
conn->dbus_conn = NULL;
free(conn);
ERR("Error registering with bus: %s", err.message);
return NULL;
}
conn->type = type;
conn->refcount = 1;
conn->shared = !!shared;
EINA_MAGIC_SET(conn, ELDBUS_CONNECTION_MAGIC);
conn->names = eina_hash_string_superfast_new(NULL);
eldbus_connection_setup(conn);
eldbus_signal_handler_add(conn, NULL, DBUS_PATH_LOCAL, DBUS_INTERFACE_LOCAL,
"Disconnected", _disconnected, conn);
obj = eldbus_object_get(conn, ELDBUS_FDO_BUS, ELDBUS_FDO_PATH);
conn->fdo_proxy = eldbus_proxy_get(obj, ELDBUS_FDO_INTERFACE);
DBG("Returned new connection at %p", conn);
return conn;
}
EAPI Eldbus_Connection *
eldbus_private_connection_get(Eldbus_Connection_Type type)
{
DBG("Getting private connection with type %d", type);
return _connection_get(type, NULL, EINA_FALSE);
}
EAPI Eldbus_Connection *
eldbus_connection_get(Eldbus_Connection_Type type)
{
Eldbus_Connection *conn;
DBG("Getting connection with type %d", type);
if (!type)
return NULL;
if (type == ELDBUS_CONNECTION_TYPE_ADDRESS)
{
ERR("CONNECTION_TYPE_ADDRESS must be used with appropriate address_connection_get() function");
return NULL;
}
conn = (Eldbus_Connection *) shared_connections[type - 1];
if (conn)
{
DBG("Connection with type %d exists at %p; reffing and returning",
type, conn);
return eldbus_connection_ref(conn);
}
conn = _connection_get(type, NULL, EINA_TRUE);
EINA_SAFETY_ON_NULL_RETURN_VAL(conn, NULL);
shared_connections[type - 1] = conn;
return conn;
}
EAPI Eldbus_Connection *
eldbus_address_connection_get(const char *address)
{
Eldbus_Connection *conn = NULL;
DBG("Getting connection with address %s", address);
if (address_connections == NULL)
{
address_connections = eina_hash_string_small_new(NULL);
shared_connections[ELDBUS_CONNECTION_TYPE_ADDRESS - 1] = address_connections;
}
else
{
conn = (Eldbus_Connection *) eina_hash_find(address_connections, address);
}
if (conn != NULL)
{
DBG("Connection with address %s exists at %p; reffing and returning",
address, conn);
return eldbus_connection_ref(conn);
}
conn = _connection_get(ELDBUS_CONNECTION_TYPE_ADDRESS, address, EINA_TRUE);
EINA_SAFETY_ON_NULL_RETURN_VAL(conn, NULL);
eina_hash_add(address_connections, address, conn);
return conn;
}
EAPI Eldbus_Connection *
eldbus_private_address_connection_get(const char *address)
{
DBG("Getting private connection with address %s", address);
return _connection_get(ELDBUS_CONNECTION_TYPE_ADDRESS, address, EINA_FALSE);
}
EAPI Eldbus_Connection *
eldbus_connection_ref(Eldbus_Connection *conn)
{
ELDBUS_CONNECTION_CHECK_RETVAL(conn, NULL);
DBG("conn=%p, pre-refcount=%d", conn, conn->refcount);
conn->refcount++;
return conn;
}
static void
_eldbus_connection_free(Eldbus_Connection *conn)
{
unsigned int i;
Eldbus_Handler_Data *fd_handler;
Eldbus_Timeout_Data *timer;
Eina_Inlist *list;
Eldbus_Signal_Handler *h;
Eldbus_Pending *p;
Eina_Iterator *iter;
Eldbus_Connection_Name *cn;
Eina_Array *cns;
const char *name;
DBG("Freeing connection %p", conn);
_eldbus_connection_event_callback_call
(conn, ELDBUS_CONNECTION_EVENT_DEL, NULL);
conn->refcount = 1;
eldbus_cbs_free_dispatch(&(conn->cbs_free), conn);
/**
* Flush all messages in outgoing queue, also this will send all
* ObjectManager and Property changed signals of all paths that
* this connection is server.
*/
dbus_connection_flush(conn->dbus_conn);
EINA_INLIST_FOREACH_SAFE(conn->pendings, list, p)
eldbus_pending_cancel(p);
cns = eina_array_new(eina_hash_population(conn->names));
iter = eina_hash_iterator_data_new(conn->names);
EINA_ITERATOR_FOREACH(iter, cn)
{
while (cn->event_handlers.list)
{
Eldbus_Connection_Context_NOC_Cb *ctx;
ctx = EINA_INLIST_CONTAINER_GET(cn->event_handlers.list,
Eldbus_Connection_Context_NOC_Cb);
cn->event_handlers.list = eina_inlist_remove(cn->event_handlers.list,
cn->event_handlers.list);
free(ctx);
}
eina_array_push(cns, eina_stringshare_add(cn->name));
}
eina_iterator_free(iter);
while ((name = eina_array_pop(cns)))
{
cn = eina_hash_find(conn->names, name);
if (cn) eldbus_connection_name_gc(conn, cn);
eina_stringshare_del(name);
}
eina_hash_free(conn->names);
eina_array_free(cns);
conn->refcount = 0;
/* after cbs_free dispatch these shouldn't exit, error if they do */
if (conn->pendings)
{
CRI("Connection %p released with live pending calls!",
conn);
EINA_INLIST_FOREACH(conn->pendings, p)
edbus: Fix bug found in refactored banshee example Refactor edbus_signal_handler_add() so internal signal handlers don't set the connection free callback. This fixes the bug in which EDBus_Connection was freeing the signal handler of EDBus_Conenction_Name ==22814== Invalid read of size 4 ==22814== at 0x40564B0: edbus_signal_handler_del (edbus_signal_handler.c:278) ==22814== by 0x4040E65: _edbus_connection_name_unref (edbus_core.c:507) ==22814== by 0x404106B: edbus_connection_name_owner_monitor (edbus_core.c:520) ==22814== by 0x4055F63: _edbus_signal_handler_clean (edbus_signal_handler.c:217) ==22814== by 0x40564F8: edbus_signal_handler_del (edbus_signal_handler.c:279) ==22814== by 0x4043088: _edbus_connection_unref (edbus_core.c:1045) ==22814== by 0x404352F: edbus_connection_unref (edbus_core.c:1105) ==22814== by 0x80498AA: main (banshee.c:233) ==22814== Address 0x44bea48 is 0 bytes inside a block of size 72 free'd ==22814== at 0x402C06C: free (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so) ==22814== by 0x4056118: _edbus_signal_handler_del (edbus_signal_handler.c:249) ==22814== by 0x4056401: edbus_signal_handler_unref (edbus_signal_handler.c:272) ==22814== by 0x4056503: edbus_signal_handler_del (edbus_signal_handler.c:280) ==22814== by 0x4043088: _edbus_connection_unref (edbus_core.c:1045) ==22814== by 0x404352F: edbus_connection_unref (edbus_core.c:1105) ==22814== by 0x80498AA: main (banshee.c:233) ==22814== CRI<22814>: src/lib/edbus_signal_handler.c:278 edbus_signal_handler_del() *** Eina Magic Check Failed !!! Input handle has already been freed! *** NAUGHTY PROGRAMMER!!! *** SPANK SPANK SPANK!!! *** Now go fix your code. Tut tut tut! Patch by: José Roberto de Souza <zehortigoza@profusion.mobi> SVN revision: 80686
2012-12-11 11:50:01 -08:00
ERR("conn=%p alive pending call=%p dest=%s path=%s %s.%s()", conn, p,
eldbus_pending_destination_get(p),
eldbus_pending_path_get(p),
eldbus_pending_interface_get(p),
eldbus_pending_method_get(p));
}
edbus: Fix bug found in refactored banshee example Refactor edbus_signal_handler_add() so internal signal handlers don't set the connection free callback. This fixes the bug in which EDBus_Connection was freeing the signal handler of EDBus_Conenction_Name ==22814== Invalid read of size 4 ==22814== at 0x40564B0: edbus_signal_handler_del (edbus_signal_handler.c:278) ==22814== by 0x4040E65: _edbus_connection_name_unref (edbus_core.c:507) ==22814== by 0x404106B: edbus_connection_name_owner_monitor (edbus_core.c:520) ==22814== by 0x4055F63: _edbus_signal_handler_clean (edbus_signal_handler.c:217) ==22814== by 0x40564F8: edbus_signal_handler_del (edbus_signal_handler.c:279) ==22814== by 0x4043088: _edbus_connection_unref (edbus_core.c:1045) ==22814== by 0x404352F: edbus_connection_unref (edbus_core.c:1105) ==22814== by 0x80498AA: main (banshee.c:233) ==22814== Address 0x44bea48 is 0 bytes inside a block of size 72 free'd ==22814== at 0x402C06C: free (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so) ==22814== by 0x4056118: _edbus_signal_handler_del (edbus_signal_handler.c:249) ==22814== by 0x4056401: edbus_signal_handler_unref (edbus_signal_handler.c:272) ==22814== by 0x4056503: edbus_signal_handler_del (edbus_signal_handler.c:280) ==22814== by 0x4043088: _edbus_connection_unref (edbus_core.c:1045) ==22814== by 0x404352F: edbus_connection_unref (edbus_core.c:1105) ==22814== by 0x80498AA: main (banshee.c:233) ==22814== CRI<22814>: src/lib/edbus_signal_handler.c:278 edbus_signal_handler_del() *** Eina Magic Check Failed !!! Input handle has already been freed! *** NAUGHTY PROGRAMMER!!! *** SPANK SPANK SPANK!!! *** Now go fix your code. Tut tut tut! Patch by: José Roberto de Souza <zehortigoza@profusion.mobi> SVN revision: 80686
2012-12-11 11:50:01 -08:00
if (conn->signal_handlers)
{
CRI("Connection %p released with live signal handlers!", conn);
edbus: Fix bug found in refactored banshee example Refactor edbus_signal_handler_add() so internal signal handlers don't set the connection free callback. This fixes the bug in which EDBus_Connection was freeing the signal handler of EDBus_Conenction_Name ==22814== Invalid read of size 4 ==22814== at 0x40564B0: edbus_signal_handler_del (edbus_signal_handler.c:278) ==22814== by 0x4040E65: _edbus_connection_name_unref (edbus_core.c:507) ==22814== by 0x404106B: edbus_connection_name_owner_monitor (edbus_core.c:520) ==22814== by 0x4055F63: _edbus_signal_handler_clean (edbus_signal_handler.c:217) ==22814== by 0x40564F8: edbus_signal_handler_del (edbus_signal_handler.c:279) ==22814== by 0x4043088: _edbus_connection_unref (edbus_core.c:1045) ==22814== by 0x404352F: edbus_connection_unref (edbus_core.c:1105) ==22814== by 0x80498AA: main (banshee.c:233) ==22814== Address 0x44bea48 is 0 bytes inside a block of size 72 free'd ==22814== at 0x402C06C: free (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so) ==22814== by 0x4056118: _edbus_signal_handler_del (edbus_signal_handler.c:249) ==22814== by 0x4056401: edbus_signal_handler_unref (edbus_signal_handler.c:272) ==22814== by 0x4056503: edbus_signal_handler_del (edbus_signal_handler.c:280) ==22814== by 0x4043088: _edbus_connection_unref (edbus_core.c:1045) ==22814== by 0x404352F: edbus_connection_unref (edbus_core.c:1105) ==22814== by 0x80498AA: main (banshee.c:233) ==22814== CRI<22814>: src/lib/edbus_signal_handler.c:278 edbus_signal_handler_del() *** Eina Magic Check Failed !!! Input handle has already been freed! *** NAUGHTY PROGRAMMER!!! *** SPANK SPANK SPANK!!! *** Now go fix your code. Tut tut tut! Patch by: José Roberto de Souza <zehortigoza@profusion.mobi> SVN revision: 80686
2012-12-11 11:50:01 -08:00
EINA_INLIST_FOREACH(conn->signal_handlers, h)
ERR("conn=%p alive signal=%p %s.%s path=%s", conn, h, h->interface,
h->member, h->path);
}
for (i = 0; i < ELDBUS_CONNECTION_EVENT_LAST; i++)
{
Eldbus_Connection_Context_Event *ce = conn->event_handlers + i;
while (ce->list)
{
Eldbus_Connection_Context_Event_Cb *ctx;
ctx = EINA_INLIST_CONTAINER_GET(ce->list,
Eldbus_Connection_Context_Event_Cb);
_eldbus_connection_context_event_cb_del(ce, ctx);
}
eina_list_free(ce->to_delete);
}
EINA_MAGIC_SET(conn, EINA_MAGIC_NONE);
//will trigger a cb_dispatch_status()
dbus_connection_close(conn->dbus_conn);
dbus_connection_unref(conn->dbus_conn);
conn->dbus_conn = NULL;
EINA_INLIST_FOREACH_SAFE(conn->fd_handlers, list, fd_handler)
eldbus_fd_handler_del(fd_handler);
EINA_INLIST_FOREACH_SAFE(conn->timeouts, list, timer)
eldbus_timeout_data_free(timer->handler);
eldbus_data_del_all(&conn->data);
if (conn->idle_enterer) ecore_idle_enterer_del(conn->idle_enterer);
if (conn->type && conn->shared)
{
if (conn->type == ELDBUS_CONNECTION_TYPE_ADDRESS)
{
if (address_connections)
eina_hash_del_by_data(address_connections, conn);
}
else if (shared_connections[conn->type - 1] == (void *) conn)
shared_connections[conn->type - 1] = NULL;
}
free(conn);
}
EAPI void
eldbus_connection_unref(Eldbus_Connection *conn)
{
ELDBUS_CONNECTION_CHECK(conn);
DBG("conn=%p, pre-refcount=%d", conn, conn->refcount);
if (--conn->refcount > 0)
return;
_eldbus_connection_free(conn);
}
EAPI void
eldbus_connection_free_cb_add(Eldbus_Connection *conn, Eldbus_Free_Cb cb, const void *data)
{
ELDBUS_CONNECTION_CHECK(conn);
EINA_SAFETY_ON_NULL_RETURN(cb);
conn->cbs_free = eldbus_cbs_free_add(conn->cbs_free, cb, data);
}
EAPI void
eldbus_connection_free_cb_del(Eldbus_Connection *conn, Eldbus_Free_Cb cb, const void *data)
{
ELDBUS_CONNECTION_CHECK(conn);
EINA_SAFETY_ON_NULL_RETURN(cb);
conn->cbs_free = eldbus_cbs_free_del(conn->cbs_free, cb, data);
}
EAPI void
eldbus_connection_data_set(Eldbus_Connection *conn, const char *key, const void *data)
{
ELDBUS_CONNECTION_CHECK(conn);
EINA_SAFETY_ON_NULL_RETURN(key);
EINA_SAFETY_ON_NULL_RETURN(data);
eldbus_data_set(&(conn->data), key, data);
}
EAPI void *
eldbus_connection_data_get(const Eldbus_Connection *conn, const char *key)
{
ELDBUS_CONNECTION_CHECK_RETVAL(conn, NULL);
EINA_SAFETY_ON_NULL_RETURN_VAL(key, NULL);
return eldbus_data_get(&(((Eldbus_Connection *)conn)->data), key);
}
EAPI void *
eldbus_connection_data_del(Eldbus_Connection *conn, const char *key)
{
ELDBUS_CONNECTION_CHECK_RETVAL(conn, NULL);
EINA_SAFETY_ON_NULL_RETURN_VAL(key, NULL);
return eldbus_data_del(&(((Eldbus_Connection *)conn)->data), key);
}
static void
eldbus_dispatch_name_owner_change(Eldbus_Connection_Name *cn, const char *old_id)
{
Eldbus_Connection_Context_NOC_Cb *ctx;
const char *previous_id = !old_id ? "" : old_id;
cn->event_handlers.walking++;
EINA_INLIST_FOREACH(cn->event_handlers.list, ctx)
{
if (ctx->deleted) continue;
if (!old_id && !ctx->allow_initial)
continue;
ctx->cb((void *)ctx->cb_data, cn->name, previous_id, cn->unique_id);
}
cn->event_handlers.walking--;
EINA_LIST_FREE(cn->event_handlers.to_delete, ctx)
{
cn->event_handlers.list = eina_inlist_remove(cn->event_handlers.list,
EINA_INLIST_GET(ctx));
free(ctx);
}
eldbus_connection_name_gc(cn->name_owner_changed->conn, cn);
}
typedef struct _dispach_name_owner_data
{
Eldbus_Connection_Context_NOC_Cb *ctx;
const Eldbus_Connection_Name *cn;
} dispatch_name_owner_data;
static Eina_Bool
dispach_name_owner_cb(void *context)
{
dispatch_name_owner_data *data = context;
data->ctx->cb((void *)data->ctx->cb_data, data->cn->name, "",
data->cn->unique_id);
data->ctx->idle_enterer = NULL;
free(data);
return ECORE_CALLBACK_CANCEL;
}
EAPI void
eldbus_name_owner_changed_callback_add(Eldbus_Connection *conn, const char *bus, Eldbus_Name_Owner_Changed_Cb cb, const void *cb_data, Eina_Bool allow_initial_call)
{
Eldbus_Connection_Name *cn;
Eldbus_Connection_Context_NOC_Cb *ctx;
ELDBUS_CONNECTION_CHECK(conn);
EINA_SAFETY_ON_NULL_RETURN(bus);
EINA_SAFETY_ON_NULL_RETURN(cb);
cn = eldbus_connection_name_get(conn, bus);
EINA_SAFETY_ON_NULL_RETURN(cn);
ctx = calloc(1, sizeof(Eldbus_Connection_Context_NOC_Cb));
EINA_SAFETY_ON_NULL_GOTO(ctx, cleanup);
ctx->cb = cb;
ctx->cb_data = cb_data;
ctx->allow_initial = allow_initial_call;
cn->event_handlers.list = eina_inlist_append(cn->event_handlers.list,
EINA_INLIST_GET(ctx));
if (cn->unique_id && allow_initial_call)
{
dispatch_name_owner_data *dispatch_data;
dispatch_data = malloc(sizeof(dispatch_name_owner_data));
EINA_SAFETY_ON_NULL_RETURN(dispatch_data);
dispatch_data->cn = cn;
dispatch_data->ctx = ctx;
ctx->idle_enterer = ecore_idle_enterer_add(dispach_name_owner_cb, dispatch_data);
}
return;
cleanup:
eldbus_connection_name_gc(conn, cn);
}
EAPI void
eldbus_name_owner_changed_callback_del(Eldbus_Connection *conn, const char *bus, Eldbus_Name_Owner_Changed_Cb cb, const void *cb_data)
{
Eldbus_Connection_Name *cn;
Eldbus_Connection_Context_NOC_Cb *iter, *found = NULL;
ELDBUS_CONNECTION_CHECK(conn);
EINA_SAFETY_ON_NULL_RETURN(bus);
EINA_SAFETY_ON_NULL_RETURN(cb);
cn = eina_hash_find(conn->names, bus);
EINA_SAFETY_ON_NULL_RETURN(cn);
EINA_INLIST_FOREACH(cn->event_handlers.list, iter)
{
if (cb != iter->cb) continue;
if ((cb_data) && (cb_data != iter->cb_data)) continue;
found = iter;
break;
}
EINA_SAFETY_ON_NULL_RETURN(found);
EINA_SAFETY_ON_TRUE_RETURN(found->deleted);
if (cn->event_handlers.walking)
{
found->deleted = EINA_TRUE;
cn->event_handlers.to_delete = eina_list_append
(cn->event_handlers.to_delete, found);
return;
}
cn->event_handlers.list = eina_inlist_remove(cn->event_handlers.list,
EINA_INLIST_GET(found));
if (found->idle_enterer)
{
dispatch_name_owner_data *data;
data = ecore_idle_enterer_del(found->idle_enterer);
free(data);
}
free(found);
eldbus_connection_name_gc(conn, cn);
}
EAPI void
eldbus_connection_event_callback_add(Eldbus_Connection *conn, Eldbus_Connection_Event_Type type, Eldbus_Connection_Event_Cb cb, const void *cb_data)
{
Eldbus_Connection_Context_Event *ce;
Eldbus_Connection_Context_Event_Cb *ctx;
ELDBUS_CONNECTION_CHECK(conn);
EINA_SAFETY_ON_NULL_RETURN(cb);
EINA_SAFETY_ON_TRUE_RETURN(type >= ELDBUS_CONNECTION_EVENT_LAST);
ce = conn->event_handlers + type;
ctx = calloc(1, sizeof(Eldbus_Connection_Context_Event_Cb));
EINA_SAFETY_ON_NULL_RETURN(ctx);
ctx->cb = cb;
ctx->cb_data = cb_data;
ce->list = eina_inlist_append(ce->list, EINA_INLIST_GET(ctx));
}
static void
_eldbus_connection_context_event_cb_del(Eldbus_Connection_Context_Event *ce, Eldbus_Connection_Context_Event_Cb *ctx)
{
ce->list = eina_inlist_remove(ce->list, EINA_INLIST_GET(ctx));
free(ctx);
}
EAPI void
eldbus_connection_event_callback_del(Eldbus_Connection *conn, Eldbus_Connection_Event_Type type, Eldbus_Connection_Event_Cb cb, const void *cb_data)
{
Eldbus_Connection_Context_Event *ce;
Eldbus_Connection_Context_Event_Cb *iter, *found = NULL;
ELDBUS_CONNECTION_CHECK(conn);
EINA_SAFETY_ON_NULL_RETURN(cb);
EINA_SAFETY_ON_TRUE_RETURN(type >= ELDBUS_CONNECTION_EVENT_LAST);
ce = conn->event_handlers + type;
EINA_INLIST_FOREACH(ce->list, iter)
{
if (cb != iter->cb) continue;
if ((cb_data) && (cb_data != iter->cb_data)) continue;
found = iter;
break;
}
EINA_SAFETY_ON_NULL_RETURN(found);
EINA_SAFETY_ON_TRUE_RETURN(found->deleted);
if (ce->walking)
{
found->deleted = EINA_TRUE;
ce->to_delete = eina_list_append(ce->to_delete, found);
return;
}
_eldbus_connection_context_event_cb_del(ce, found);
}
static void
_eldbus_connection_event_callback_call(Eldbus_Connection *conn, Eldbus_Connection_Event_Type type, const void *event_info)
{
Eldbus_Connection_Context_Event *ce;
Eldbus_Connection_Context_Event_Cb *iter;
ce = conn->event_handlers + type;
ce->walking++;
EINA_INLIST_FOREACH(ce->list, iter)
{
if (iter->deleted) continue;
iter->cb((void *)iter->cb_data, conn, (void *)event_info);
}
ce->walking--;
if (ce->walking > 0) return;
EINA_LIST_FREE(ce->to_delete, iter)
_eldbus_connection_context_event_cb_del(ce, iter);
}
void
eldbus_connection_event_callback_call(Eldbus_Connection *conn, Eldbus_Connection_Event_Type type, const void *event_info)
{
ELDBUS_CONNECTION_CHECK(conn);
EINA_SAFETY_ON_TRUE_RETURN(type >= ELDBUS_CONNECTION_EVENT_LAST);
EINA_SAFETY_ON_TRUE_RETURN(type == ELDBUS_CONNECTION_EVENT_DEL);
_eldbus_connection_event_callback_call(conn, type, event_info);
}
void
eldbus_connection_signal_handler_add(Eldbus_Connection *conn, Eldbus_Signal_Handler *handler)
{
ELDBUS_CONNECTION_CHECK(conn);
EINA_SAFETY_ON_NULL_RETURN(handler);
conn->signal_handlers = eina_inlist_append(conn->signal_handlers,
EINA_INLIST_GET(handler));
}
void
eldbus_connection_pending_add(Eldbus_Connection *conn, Eldbus_Pending *pending)
{
ELDBUS_CONNECTION_CHECK(conn);
EINA_SAFETY_ON_NULL_RETURN(pending);
conn->pendings = eina_inlist_append(conn->pendings,
EINA_INLIST_GET(pending));
}
void
eldbus_connection_signal_handler_del(Eldbus_Connection *conn, Eldbus_Signal_Handler *handler)
{
EINA_SAFETY_ON_NULL_RETURN(conn);
EINA_SAFETY_ON_NULL_RETURN(handler);
conn->signal_handlers = eina_inlist_remove(conn->signal_handlers,
EINA_INLIST_GET(handler));
}
void
eldbus_connection_pending_del(Eldbus_Connection *conn, Eldbus_Pending *pending)
{
EINA_SAFETY_ON_NULL_RETURN(conn);
EINA_SAFETY_ON_NULL_RETURN(pending);
conn->pendings = eina_inlist_remove(conn->pendings,
EINA_INLIST_GET(pending));
}
const char *
eldbus_connection_unique_name_get(Eldbus_Connection *conn)
{
ELDBUS_CONNECTION_CHECK_RETVAL(conn, NULL);
return dbus_bus_get_unique_name(conn->dbus_conn);
}