X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=gdbus%2Fwatch.c;h=1ca3c4bda3cb0f7a614ed0e4367ec64fa0cd4807;hb=d180a9d2152fdd28ea68f0fbe0ef52e9c7ceac5a;hp=42e158febd535b9c7188f508933995a57711c368;hpb=3999fdc6a0453f523fd92ad67e314c6a77fc26a4;p=platform%2Fupstream%2Fconnman.git diff --git a/gdbus/watch.c b/gdbus/watch.c old mode 100644 new mode 100755 index 42e158f..1ca3c4b --- a/gdbus/watch.c +++ b/gdbus/watch.c @@ -2,7 +2,7 @@ * * D-Bus helper library * - * Copyright (C) 2004-2010 Marcel Holtmann + * Copyright (C) 2004-2011 Marcel Holtmann * * * This program is free software; you can redistribute it and/or modify @@ -43,11 +43,21 @@ static DBusHandlerResult message_filter(DBusConnection *connection, static guint listener_id = 0; static GSList *listeners = NULL; +struct service_data { + DBusConnection *conn; + DBusPendingCall *call; + char *name; + const char *owner; + guint id; + struct filter_callback *callback; +}; + struct filter_callback { GDBusWatchFunction conn_func; GDBusWatchFunction disc_func; GDBusSignalFunction signal_func; GDBusDestroyFunction destroy_func; + struct service_data *data; void *user_data; guint id; }; @@ -55,19 +65,22 @@ struct filter_callback { struct filter_data { DBusConnection *connection; DBusHandleMessageFunction handle_func; - char *sender; + char *name; + char *owner; char *path; char *interface; char *member; char *argument; GSList *callbacks; GSList *processed; + guint name_watch; gboolean lock; gboolean registered; }; -static struct filter_data *filter_data_find(DBusConnection *connection, - const char *sender, +static struct filter_data *filter_data_find_match(DBusConnection *connection, + const char *name, + const char *owner, const char *path, const char *interface, const char *member, @@ -82,24 +95,39 @@ static struct filter_data *filter_data_find(DBusConnection *connection, if (connection != data->connection) continue; - if (sender && data->sender && - g_str_equal(sender, data->sender) == FALSE) + if (g_strcmp0(name, data->name) != 0) continue; - if (path && data->path && - g_str_equal(path, data->path) == FALSE) + if (g_strcmp0(owner, data->owner) != 0) continue; - if (interface && data->interface && - g_str_equal(interface, data->interface) == FALSE) + if (g_strcmp0(path, data->path) != 0) continue; - if (member && data->member && - g_str_equal(member, data->member) == FALSE) + if (g_strcmp0(interface, data->interface) != 0) continue; - if (argument && data->argument && - g_str_equal(argument, data->argument) == FALSE) + if (g_strcmp0(member, data->member) != 0) + continue; + + if (g_strcmp0(argument, data->argument) != 0) + continue; + + return data; + } + + return NULL; +} + +static struct filter_data *filter_data_find(DBusConnection *connection) +{ + GSList *current; + + for (current = listeners; + current != NULL; current = current->next) { + struct filter_data *data = current->data; + + if (connection != data->connection) continue; return data; @@ -108,15 +136,62 @@ static struct filter_data *filter_data_find(DBusConnection *connection, return NULL; } +#if defined TIZEN_EXT +#define SENDER_PREFIX ",sender='%s'" +#define PATH_PREFIX ",path='%s'" +#define IFACE_PREFIX ",interface='%s'" +#define MEMBER_PREFIX ",member='%s'" +#define ARG0_PREFIX ",arg0='%s'" + +static gboolean check_rule_length(int remains, const char *prefix, const char *data) +{ + if (!prefix || !data) + return FALSE; + + return strlen(prefix) - 2 + strlen(data) < remains; +} + +static void format_rule(struct filter_data *data, char *rule, size_t size) +{ + const char *sender; + int offset; + + offset = snprintf(rule, size, "type='signal'"); + sender = data->name ? : data->owner; + + if (sender && + check_rule_length(size - offset, SENDER_PREFIX, sender)) + offset += snprintf(rule + offset, size - offset, + SENDER_PREFIX, sender); + if (data->path && + check_rule_length(size - offset, PATH_PREFIX, data->path)) + offset += snprintf(rule + offset, size - offset, + PATH_PREFIX, data->path); + if (data->interface && + check_rule_length(size - offset, IFACE_PREFIX, data->interface)) + offset += snprintf(rule + offset, size - offset, + IFACE_PREFIX, data->interface); + if (data->member && + check_rule_length(size - offset, MEMBER_PREFIX, data->member)) + offset += snprintf(rule + offset, size - offset, + MEMBER_PREFIX, data->member); + if (data->argument && + check_rule_length(size - offset, ARG0_PREFIX, data->argument)) + snprintf(rule + offset, size - offset, + ARG0_PREFIX, data->argument); +} +#else static void format_rule(struct filter_data *data, char *rule, size_t size) { + const char *sender; int offset; offset = snprintf(rule, size, "type='signal'"); + sender = data->name ? : data->owner; - if (data->sender) + if (sender) offset += snprintf(rule + offset, size - offset, - ",sender='%s'", data->sender); + ",sender='%s'", sender); if (data->path) offset += snprintf(rule + offset, size - offset, ",path='%s'", data->path); @@ -130,6 +205,7 @@ static void format_rule(struct filter_data *data, char *rule, size_t size) snprintf(rule + offset, size - offset, ",arg0='%s'", data->argument); } +#endif static gboolean add_match(struct filter_data *data, DBusHandleMessageFunction filter) @@ -174,6 +250,30 @@ static gboolean remove_match(struct filter_data *data) return TRUE; } +static void filter_data_free(struct filter_data *data) +{ + GSList *l; + + /* Remove filter if there are no listeners left for the connection */ + if (filter_data_find(data->connection) == NULL) + dbus_connection_remove_filter(data->connection, message_filter, + NULL); + + for (l = data->callbacks; l != NULL; l = l->next) + g_free(l->data); + + g_slist_free(data->callbacks); + g_dbus_remove_watch(data->connection, data->name_watch); + g_free(data->name); + g_free(data->owner); + g_free(data->path); + g_free(data->interface); + g_free(data->member); + g_free(data->argument); + dbus_connection_unref(data->connection); + g_free(data); +} + static struct filter_data *filter_data_get(DBusConnection *connection, DBusHandleMessageFunction filter, const char *sender, @@ -183,8 +283,9 @@ static struct filter_data *filter_data_get(DBusConnection *connection, const char *argument) { struct filter_data *data; + const char *name = NULL, *owner = NULL; - if (!filter_data_find(connection, NULL, NULL, NULL, NULL, NULL)) { + if (filter_data_find(connection) == NULL) { if (!dbus_connection_add_filter(connection, message_filter, NULL, NULL)) { error("dbus_connection_add_filter() failed"); @@ -192,22 +293,32 @@ static struct filter_data *filter_data_get(DBusConnection *connection, } } - data = filter_data_find(connection, sender, path, interface, member, - argument); + if (sender == NULL) + goto proceed; + + if (sender[0] == ':') + owner = sender; + else + name = sender; + +proceed: + data = filter_data_find_match(connection, name, owner, path, + interface, member, argument); if (data) return data; data = g_new0(struct filter_data, 1); data->connection = dbus_connection_ref(connection); - data->sender = g_strdup(sender); + data->name = g_strdup(name); + data->owner = g_strdup(owner); data->path = g_strdup(path); data->interface = g_strdup(interface); data->member = g_strdup(member); data->argument = g_strdup(argument); if (!add_match(data, filter)) { - g_free(data); + filter_data_free(data); return NULL; } @@ -236,23 +347,6 @@ static struct filter_callback *filter_data_find_callback( return NULL; } -static void filter_data_free(struct filter_data *data) -{ - GSList *l; - - for (l = data->callbacks; l != NULL; l = l->next) - g_free(l->data); - - g_slist_free(data->callbacks); - g_free(data->sender); - g_free(data->path); - g_free(data->interface); - g_free(data->member); - g_free(data->argument); - dbus_connection_unref(data->connection); - g_free(data); -} - static void filter_data_call_and_free(struct filter_data *data) { GSList *l; @@ -279,7 +373,7 @@ static struct filter_callback *filter_data_add_callback( { struct filter_callback *cb = NULL; - cb = g_new(struct filter_callback, 1); + cb = g_new0(struct filter_callback, 1); cb->conn_func = connect; cb->disc_func = disconnect; @@ -296,12 +390,38 @@ static struct filter_callback *filter_data_add_callback( return cb; } +static void service_data_free(struct service_data *data) +{ + struct filter_callback *callback = data->callback; + + dbus_connection_unref(data->conn); + + if (data->call) + dbus_pending_call_unref(data->call); + + if (data->id) + g_source_remove(data->id); + + g_free(data->name); + g_free(data); + + callback->data = NULL; +} + +/* Returns TRUE if data is freed */ static gboolean filter_data_remove_callback(struct filter_data *data, struct filter_callback *cb) { data->callbacks = g_slist_remove(data->callbacks, cb); data->processed = g_slist_remove(data->processed, cb); + /* Cancel pending operations */ + if (cb->data) { + if (cb->data->call) + dbus_pending_call_cancel(cb->data->call); + service_data_free(cb->data); + } + if (cb->destroy_func) cb->destroy_func(cb->user_data); @@ -310,18 +430,11 @@ static gboolean filter_data_remove_callback(struct filter_data *data, /* Don't remove the filter if other callbacks exist or data is lock * processing callbacks */ if (data->callbacks || data->lock) - return TRUE; + return FALSE; if (data->registered && !remove_match(data)) return FALSE; - /* Remove filter if there are no listeners left for the connection */ - data = filter_data_find(data->connection, NULL, NULL, NULL, NULL, - NULL); - if (!data) - dbus_connection_remove_filter(data->connection, message_filter, - NULL); - listeners = g_slist_remove(listeners, data); filter_data_free(data); @@ -339,7 +452,9 @@ static DBusHandlerResult signal_filter(DBusConnection *connection, if (cb->signal_func && !cb->signal_func(connection, message, cb->user_data)) { - filter_data_remove_callback(data, cb); + if (filter_data_remove_callback(data, cb)) + break; + continue; } @@ -355,6 +470,37 @@ static DBusHandlerResult signal_filter(DBusConnection *connection, return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } +static void update_name_cache(const char *name, const char *owner) +{ + GSList *l; + + for (l = listeners; l != NULL; l = l->next) { + struct filter_data *data = l->data; + + if (g_strcmp0(data->name, name) != 0) + continue; + + g_free(data->owner); + data->owner = g_strdup(owner); + } +} + +static const char *check_name_cache(const char *name) +{ + GSList *l; + + for (l = listeners; l != NULL; l = l->next) { + struct filter_data *data = l->data; + + if (g_strcmp0(data->name, name) != 0) + continue; + + return data->owner; + } + + return NULL; +} + static DBusHandlerResult service_filter(DBusConnection *connection, DBusMessage *message, void *user_data) { @@ -371,6 +517,8 @@ static DBusHandlerResult service_filter(DBusConnection *connection, return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } + update_name_cache(name, new); + while (data->callbacks) { cb = data->callbacks->data; @@ -387,13 +535,16 @@ static DBusHandlerResult service_filter(DBusConnection *connection, if (!g_slist_find(data->callbacks, cb)) continue; - data->callbacks = g_slist_remove(data->callbacks, cb); + /* Only auto remove if it is a bus name watch */ + if (data->argument[0] == ':' && + (cb->conn_func == NULL || cb->disc_func == NULL)) { + if (filter_data_remove_callback(data, cb)) + break; - if (!cb->conn_func || !cb->disc_func) { - g_free(cb); continue; } + data->callbacks = g_slist_remove(data->callbacks, cb); data->processed = g_slist_append(data->processed, cb); } @@ -406,6 +557,7 @@ static DBusHandlerResult message_filter(DBusConnection *connection, { struct filter_data *data; const char *sender, *path, *iface, *member, *arg = NULL; + GSList *current, *delete_listener = NULL; /* Only filter signals */ if (dbus_message_get_type(message) != DBUS_MESSAGE_TYPE_SIGNAL) @@ -417,82 +569,127 @@ static DBusHandlerResult message_filter(DBusConnection *connection, member = dbus_message_get_member(message); dbus_message_get_args(message, NULL, DBUS_TYPE_STRING, &arg, DBUS_TYPE_INVALID); - data = filter_data_find(connection, sender, path, iface, member, arg); - if (!data) { - error("Got %s.%s signal which has no listeners", iface, member); - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - } + /* If sender != NULL it is always the owner */ + + for (current = listeners; current != NULL; current = current->next) { + data = current->data; + + if (connection != data->connection) + continue; + + if (!sender && data->owner) + continue; + + if (data->owner && g_str_equal(sender, data->owner) == FALSE) + continue; + + if (data->path && g_str_equal(path, data->path) == FALSE) + continue; + + if (data->interface && g_str_equal(iface, + data->interface) == FALSE) + continue; + + if (data->member && g_str_equal(member, data->member) == FALSE) + continue; + + if (data->argument && g_str_equal(arg, + data->argument) == FALSE) + continue; + + if (data->handle_func) { + data->lock = TRUE; - if (data->handle_func) { - data->lock = TRUE; + data->handle_func(connection, message, data); - data->handle_func(connection, message, data); + data->callbacks = data->processed; + data->processed = NULL; + data->lock = FALSE; + } - data->callbacks = data->processed; - data->processed = NULL; - data->lock = FALSE; + if (!data->callbacks) + delete_listener = g_slist_prepend(delete_listener, + current); } - if (data->callbacks) + if (delete_listener == NULL) return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - remove_match(data); + for (current = delete_listener; current != NULL; + current = delete_listener->next) { + GSList *l = current->data; - listeners = g_slist_remove(listeners, data); - filter_data_free(data); + data = l->data; - /* Remove filter if there no listener left for the connection */ - data = filter_data_find(connection, NULL, NULL, NULL, NULL, NULL); - if (!data) - dbus_connection_remove_filter(connection, message_filter, - NULL); + /* Has any other callback added callbacks back to this data? */ + if (data->callbacks != NULL) + continue; + + remove_match(data); + listeners = g_slist_delete_link(listeners, l); + + filter_data_free(data); + } + + g_slist_free(delete_listener); return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } -struct service_data { +static gboolean update_service(void *user_data) +{ + struct service_data *data = user_data; + struct filter_callback *cb = data->callback; DBusConnection *conn; - GDBusWatchFunction conn_func; - void *user_data; -}; + + conn = dbus_connection_ref(data->conn); + service_data_free(data); + + if (cb->conn_func) + cb->conn_func(conn, cb->user_data); + + dbus_connection_unref(conn); + + return FALSE; +} static void service_reply(DBusPendingCall *call, void *user_data) { struct service_data *data = user_data; DBusMessage *reply; - DBusError error; - dbus_bool_t has_owner; + DBusError err; reply = dbus_pending_call_steal_reply(call); if (reply == NULL) return; - dbus_error_init(&error); + dbus_error_init(&err); + + if (dbus_set_error_from_message(&err, reply)) + goto fail; - if (dbus_message_get_args(reply, &error, - DBUS_TYPE_BOOLEAN, &has_owner, - DBUS_TYPE_INVALID) == FALSE) { - if (dbus_error_is_set(&error) == TRUE) { - error("%s", error.message); - dbus_error_free(&error); - } else { - error("Wrong arguments for NameHasOwner reply"); - } - goto done; - } + if (dbus_message_get_args(reply, &err, + DBUS_TYPE_STRING, &data->owner, + DBUS_TYPE_INVALID) == FALSE) + goto fail; + + update_service(data); - if (has_owner && data->conn_func) - data->conn_func(data->conn, data->user_data); + goto done; +fail: + error("%s", err.message); + dbus_error_free(&err); + service_data_free(data); done: dbus_message_unref(reply); } -static void check_service(DBusConnection *connection, const char *name, - GDBusWatchFunction connect, void *user_data) +static void check_service(DBusConnection *connection, + const char *name, + struct filter_callback *callback) { DBusMessage *message; - DBusPendingCall *call; struct service_data *data; data = g_try_malloc0(sizeof(*data)); @@ -501,12 +698,19 @@ static void check_service(DBusConnection *connection, const char *name, return; } - data->conn = connection; - data->conn_func = connect; - data->user_data = user_data; + data->conn = dbus_connection_ref(connection); + data->name = g_strdup(name); + data->callback = callback; + callback->data = data; + + data->owner = check_name_cache(name); + if (data->owner != NULL) { + data->id = g_idle_add(update_service, data); + return; + } message = dbus_message_new_method_call(DBUS_SERVICE_DBUS, - DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS, "NameHasOwner"); + DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS, "GetNameOwner"); if (message == NULL) { error("Can't allocate new message"); g_free(data); @@ -517,19 +721,19 @@ static void check_service(DBusConnection *connection, const char *name, DBUS_TYPE_INVALID); if (dbus_connection_send_with_reply(connection, message, - &call, -1) == FALSE) { + &data->call, -1) == FALSE) { error("Failed to execute method call"); g_free(data); goto done; } - if (call == NULL) { + if (data->call == NULL) { error("D-Bus connection not available"); g_free(data); goto done; } - dbus_pending_call_set_notify(call, service_reply, data, NULL); + dbus_pending_call_set_notify(data->call, service_reply, data, NULL); done: dbus_message_unref(message); @@ -543,22 +747,23 @@ guint g_dbus_add_service_watch(DBusConnection *connection, const char *name, struct filter_data *data; struct filter_callback *cb; - if (!name) + if (name == NULL) return 0; - data = filter_data_get(connection, service_filter, NULL, NULL, + data = filter_data_get(connection, service_filter, + DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS, "NameOwnerChanged", name); - if (!data) + if (data == NULL) return 0; - cb = filter_data_add_callback(data, connect, disconnect, NULL, NULL, + cb = filter_data_add_callback(data, connect, disconnect, NULL, destroy, user_data); - if (!cb) + if (cb == NULL) return 0; if (connect) - check_service(connection, name, connect, user_data); + check_service(connection, name, cb); return cb->id; } @@ -582,14 +787,47 @@ guint g_dbus_add_signal_watch(DBusConnection *connection, data = filter_data_get(connection, signal_filter, sender, path, interface, member, NULL); - if (!data) + if (data == NULL) + return 0; + + cb = filter_data_add_callback(data, NULL, NULL, function, destroy, + user_data); + if (cb == NULL) + return 0; + + if (data->name != NULL && data->name_watch == 0) + data->name_watch = g_dbus_add_service_watch(connection, + data->name, NULL, + NULL, NULL, NULL); + + return cb->id; +} + +guint g_dbus_add_properties_watch(DBusConnection *connection, + const char *sender, const char *path, + const char *interface, + GDBusSignalFunction function, void *user_data, + GDBusDestroyFunction destroy) +{ + struct filter_data *data; + struct filter_callback *cb; + + data = filter_data_get(connection, signal_filter, sender, path, + DBUS_INTERFACE_PROPERTIES, "PropertiesChanged", + interface); + if (data == NULL) return 0; cb = filter_data_add_callback(data, NULL, NULL, function, destroy, user_data); - if (!cb) + if (cb == NULL) return 0; + if (data->name != NULL && data->name_watch == 0) + data->name_watch = g_dbus_add_service_watch(connection, + data->name, NULL, + NULL, NULL, NULL); + return cb->id; } @@ -619,10 +857,8 @@ void g_dbus_remove_all_watches(DBusConnection *connection) { struct filter_data *data; - while ((data = filter_data_find(connection, NULL, NULL, NULL, NULL, NULL))) { + while ((data = filter_data_find(connection))) { listeners = g_slist_remove(listeners, data); filter_data_call_and_free(data); } - - dbus_connection_remove_filter(connection, message_filter, NULL); }