From: Denis Kenzior <denkenz@gmail.com>
To: ell@lists.01.org
Subject: Re: [PATCH 2/3] dbus: Add disconnect watch support
Date: Tue, 10 Feb 2015 16:02:34 -0600 [thread overview]
Message-ID: <54DA7FFA.8050003@gmail.com> (raw)
In-Reply-To: <1423583207-22917-3-git-send-email-jukka.rissanen@linux.intel.com>
[-- Attachment #1: Type: text/plain, Size: 11296 bytes --]
Hi Jukka,
On 02/10/2015 09:46 AM, Jukka Rissanen wrote:
> Allow caller to monitor the disconnect status of a service.
> ---
> Makefile.am | 1 +
> ell/dbus-watch.c | 267 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
> ell/dbus.h | 17 ++++
> 3 files changed, 285 insertions(+)
> create mode 100644 ell/dbus-watch.c
>
> diff --git a/Makefile.am b/Makefile.am
> index 50f81eb..3dcc7ee 100644
> --- a/Makefile.am
> +++ b/Makefile.am
> @@ -63,6 +63,7 @@ ell_libell_la_SOURCES = $(linux_headers) \
> ell/dbus-message.c \
> ell/dbus-util.c \
> ell/dbus-service.c \
> + ell/dbus-watch.c \
> ell/gvariant-private.h \
> ell/gvariant-util.c \
> ell/siphash-private.h \
> diff --git a/ell/dbus-watch.c b/ell/dbus-watch.c
> new file mode 100644
> index 0000000..005bc83
> --- /dev/null
> +++ b/ell/dbus-watch.c
> @@ -0,0 +1,267 @@
> +/*
> + *
> + * Embedded Linux library
> + *
> + * Copyright (C) 2015 Intel Corporation. All rights reserved.
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * This library 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
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; 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
> +
> +#define _GNU_SOURCE
> +#include <stdarg.h>
> +#include <string.h>
> +#include <stdio.h>
> +#include <errno.h>
> +
> +#include "log.h"
> +#include "util.h"
> +#include "queue.h"
> +#include "string.h"
> +#include "dbus.h"
> +#include "dbus-private.h"
> +#include "private.h"
> +
> +struct filter_data {
> + struct l_dbus *dbus;
> + l_dbus_message_func_t handle_func;
> + l_dbus_watch_func_t connect_func;
> + l_dbus_watch_func_t disconnect_func;
> + char *name;
> + char *owner;
> + char *path;
> + char *interface;
> + char *member;
> + char *argument;
> + void *user_data;
> + l_dbus_destroy_func_t destroy_func;
> +};
> +
> +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)
> + offset += snprintf(rule + offset, size - offset,
> + ",sender='%s'", sender);
> + if (data->path)
> + offset += snprintf(rule + offset, size - offset,
> + ",path='%s'", data->path);
> + if (data->interface)
> + offset += snprintf(rule + offset, size - offset,
> + ",interface='%s'", data->interface);
> + if (data->member)
> + offset += snprintf(rule + offset, size - offset,
> + ",member='%s'", data->member);
> + if (data->argument)
> + snprintf(rule + offset, size - offset,
> + ",arg0='%s'", data->argument);
> +}
> +
> +static void add_match(struct filter_data *data, l_dbus_message_func_t filter)
> +{
> + char rule[DBUS_MAXIMUM_MATCH_RULE_LENGTH];
> +
> + format_rule(data, rule, sizeof(rule));
> +
> + l_dbus_bus_add_match(data->dbus, rule);
> +
> + data->handle_func = filter;
> +}
> +
> +static void remove_match(struct filter_data *data)
> +{
> + char rule[DBUS_MAXIMUM_MATCH_RULE_LENGTH];
> +
> + format_rule(data, rule, sizeof(rule));
> +
> + l_dbus_bus_remove_match(data->dbus, rule);
> +}
> +
> +static struct filter_data *filter_data_get(struct l_dbus *dbus,
> + l_dbus_message_func_t filter,
> + const char *sender,
> + const char *path,
> + const char *interface,
> + const char *member,
> + const char *argument,
> + l_dbus_watch_func_t connect_func,
> + l_dbus_watch_func_t disconnect_func,
> + void *user_data,
> + l_dbus_destroy_func_t destroy)
> +{
> + struct filter_data *data;
> + const char *name = NULL, *owner = NULL;
> +
> + if (sender) {
> + if (sender[0] == ':')
> + owner = sender;
> + else
> + name = sender;
> + }
> +
> + data = l_new(struct filter_data, 1);
> +
> + data->dbus = dbus;
> + data->handle_func = filter;
> + data->connect_func = connect_func;
> + data->disconnect_func = disconnect_func;
> + data->name = l_strdup(name);
> + data->owner = l_strdup(owner);
> + data->path = l_strdup(path);
> + data->interface = l_strdup(interface);
> + data->member = l_strdup(member);
> + data->argument = l_strdup(argument);
> + data->user_data = user_data;
> + data->destroy_func = destroy;
> +
> + add_match(data, filter);
> +
> + return data;
> +}
> +
> +static void filter_data_destroy(void *user_data)
> +{
> + struct filter_data *data = user_data;
> +
> + remove_match(data);
> +
> + l_free(data->name);
> + l_free(data->owner);
> + l_free(data->path);
> + l_free(data->interface);
> + l_free(data->member);
> + l_free(data->argument);
> +
> + if (data->destroy_func)
> + data->destroy_func(data->user_data);
> +
> + l_free(data);
> +}
> +
> +static void message_filter(struct l_dbus_message *message, void *user_data)
> +{
> + struct filter_data *data = user_data;
> + const char *sender, *path, *iface, *member, *arg = NULL;
> +
> + if (_dbus_message_get_type(message) != DBUS_MESSAGE_TYPE_SIGNAL)
> + return;
> +
> + sender = l_dbus_message_get_sender(message);
> + if (!sender)
> + return;
> +
> + path = l_dbus_message_get_path(message);
> + iface = l_dbus_message_get_interface(message);
> + member = l_dbus_message_get_member(message);
> +
> + l_dbus_message_get_arguments(message, "s", &arg);
> +
> + if (data->owner && strcmp(sender, data->owner))
> + goto out;
> +
> + if (data->path && strcmp(path, data->path))
> + goto out;
> +
> + if (data->interface && strcmp(iface, data->interface))
> + goto out;
> +
> + if (data->member && strcmp(member, data->member))
> + goto out;
> +
> + if (arg && data->argument && strcmp(arg, data->argument))
> + goto out;
> +
> + if (data->handle_func)
> + data->handle_func(message, data);
> +
> +out:
> + return;
> +}
> +
> +static void service_filter(struct l_dbus_message *message, void *user_data)
> +{
> + struct filter_data *data = user_data;
> + char *name, *old, *new;
> +
> + if (!l_dbus_message_get_arguments(message, "sss",
> + &name, &old, &new)) {
> + l_error("Invalid arguments for NameOwnerChanged signal");
> + return;
> + }
> +
> + if (*new == '\0') {
> + if (data->disconnect_func)
> + data->disconnect_func(data->dbus, data->user_data);
> + } else {
> + if (data->connect_func)
> + data->connect_func(data->dbus, data->user_data);
> + }
> +}
> +
> +LIB_EXPORT unsigned int l_dbus_add_service_watch(struct l_dbus *dbus,
> + const char *name,
> + l_dbus_watch_func_t connect_func,
> + l_dbus_watch_func_t disconnect_func,
> + void *user_data,
> + l_dbus_destroy_func_t destroy)
> +{
> + struct filter_data *data;
> +
> + if (!name)
> + return 0;
> +
> + if (connect_func) {
> + l_error("Connect watch not yet supported");
> + return -EOPNOTSUPP;
> + }
> +
> + data = filter_data_get(dbus, service_filter,
> + DBUS_SERVICE_DBUS, DBUS_PATH_DBUS,
> + DBUS_INTERFACE_DBUS, "NameOwnerChanged",
> + name,
> + connect_func,
> + disconnect_func,
> + user_data,
> + destroy);
> + if (!data)
> + return 0;
> +
> + return l_dbus_register(dbus, message_filter, data,
> + filter_data_destroy);
So this is the reason why you have re-entrancy problems. You are trying
to use l_dbus_register and l_dbus_unregister. So when the signal_list
hash is traversed, the agent tries to call l_dbus_unregister and things die.
I suggest that you rework this part to maintain its own data structure
instead of using l_dbus_register, or make that part smarter. You can
handle re-entrancy like gdbus does now. Since you already used most of
the gdbus design, might as well take the rest of it (e.g. maintain a
list of items to be removed and a re-entrancy guard).
Another idea might be to use the return value from the callback in order
to remove the watch right then and there. Similar to how g_io_add_watch
and its callback works.
However, as I mentioned before, we need a way smarter data structure
here long term. A multi-level tree or something else. The current
l_hashmap_foreach traversal of signal_list needs to go away.
For the purposes of getting something working in the near future, I'm
fine with this approach.
> +}
> +
> +LIB_EXPORT unsigned int l_dbus_add_disconnect_watch(struct l_dbus *dbus,
> + const char *name,
> + l_dbus_watch_func_t disconnect_func,
> + void *user_data,
> + l_dbus_destroy_func_t destroy)
> +{
> + return l_dbus_add_service_watch(dbus, name, NULL, disconnect_func,
> + user_data, destroy);
> +}
> +
> +LIB_EXPORT bool l_dbus_remove_watch(struct l_dbus *dbus, unsigned int id)
> +{
> + return l_dbus_unregister(dbus, id);
> +}
These three functions should probably be in dbus.c and the needed
utilities exposed via dbus-private.h.
> diff --git a/ell/dbus.h b/ell/dbus.h
> index 9fcce03..8952dcf 100644
> --- a/ell/dbus.h
> +++ b/ell/dbus.h
> @@ -36,6 +36,8 @@ extern "C" {
> #define DBUS_PATH_DBUS "/org/freedesktop/DBus"
> #define DBUS_INTERFACE_DBUS "org.freedesktop.DBus"
>
> +#define DBUS_MAXIMUM_MATCH_RULE_LENGTH 1024
> +
> enum l_dbus_bus {
> L_DBUS_SYSTEM_BUS,
> L_DBUS_SESSION_BUS,
> @@ -52,6 +54,8 @@ typedef void (*l_dbus_debug_func_t) (const char *str, void *user_data);
> typedef void (*l_dbus_destroy_func_t) (void *user_data);
> typedef void (*l_dbus_interface_setup_func_t) (struct l_dbus_interface *);
>
> +typedef void (*l_dbus_watch_func_t) (struct l_dbus *dbus, void *user_data);
> +
> struct l_dbus *l_dbus_new(const char *address);
> struct l_dbus *l_dbus_new_default(enum l_dbus_bus bus);
> void l_dbus_destroy(struct l_dbus *dbus);
> @@ -204,6 +208,19 @@ bool l_dbus_unregister_interface(struct l_dbus *dbus, const char *path,
> void l_dbus_bus_add_match(struct l_dbus *dbus, const char *rule);
> void l_dbus_bus_remove_match(struct l_dbus *dbus, const char *rule);
>
> +unsigned int l_dbus_add_service_watch(struct l_dbus *dbus,
> + const char *name,
> + l_dbus_watch_func_t connect_func,
> + l_dbus_watch_func_t disconnect_func,
> + void *user_data,
> + l_dbus_destroy_func_t destroy);
> +unsigned int l_dbus_add_disconnect_watch(struct l_dbus *dbus,
> + const char *name,
> + l_dbus_watch_func_t disconnect_func,
> + void *user_data,
> + l_dbus_destroy_func_t destroy);
> +bool l_dbus_remove_watch(struct l_dbus *dbus, unsigned int id);
> +
> #ifdef __cplusplus
> }
> #endif
>
Regards,
-Denis
next prev parent reply other threads:[~2015-02-10 22:02 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
2015-02-10 15:46 [PATCH 0/3] Add DBus disconnect watch support Jukka Rissanen
2015-02-10 15:46 ` [PATCH 1/3] dbus: Add AddMatch and RemoveMatch support Jukka Rissanen
2015-02-10 15:46 ` [PATCH 2/3] dbus: Add disconnect watch support Jukka Rissanen
2015-02-10 22:02 ` Denis Kenzior [this message]
2015-02-10 15:46 ` [PATCH 3/3] unit: dbus: Add test for disconnect watch API Jukka Rissanen
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=54DA7FFA.8050003@gmail.com \
--to=denkenz@gmail.com \
--cc=ell@lists.01.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.