/* * debug-client.c - proxy for Telepathy debug objects * * Copyright © 2010 Collabora Ltd. * * 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 */ #include "config.h" #include #include #include #include #include #include #include #define DEBUG_FLAG TP_DEBUG_DEBUGGER #include "telepathy-glib/debug-internal.h" #include "telepathy-glib/proxy-internal.h" #include "telepathy-glib/util-internal.h" #include "telepathy-glib/_gen/tp-cli-debug-body.h" /** * SECTION:debug-client * @title: TpDebugClient * @short_description: proxy objects for Telepathy debug information * @see_also: #TpProxy * * This module provides access to the auxiliary objects used to * implement #TpSvcDebug. * * Since: 0.19.0 */ /** * TpDebugClientClass: * * The class of a #TpDebugClient. * * Since: 0.19.0 */ struct _TpDebugClientClass { TpProxyClass parent_class; /**/ gpointer priv; }; /** * TpDebugClient: * * A proxy object for the debug interface of a Telepathy component. * * Since: 0.19.0 */ struct _TpDebugClient { TpProxy parent; /**/ TpDebugClientPrivate *priv; }; struct _TpDebugClientPrivate { gboolean enabled; }; static const TpProxyFeature *tp_debug_client_list_features ( TpProxyClass *klass); static void tp_debug_client_prepare_core (TpDebugClient *self); static void name_owner_changed_cb (TpDBusDaemon *bus, const gchar *name, const gchar *new_owner, gpointer user_data); G_DEFINE_TYPE (TpDebugClient, tp_debug_client, TP_TYPE_PROXY) enum { PROP_ENABLED = 1 }; enum { SIG_NEW_DEBUG_MESSAGE, LAST_SIGNAL }; static guint signals[LAST_SIGNAL]; static void tp_debug_client_init (TpDebugClient *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, TP_TYPE_DEBUG_CLIENT, TpDebugClientPrivate); } static void tp_debug_client_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { TpDebugClient *self = TP_DEBUG_CLIENT (object); switch (property_id) { case PROP_ENABLED: g_value_set_boolean (value, self->priv->enabled); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void new_debug_message_cb (TpDebugClient *self, gdouble timestamp, const gchar *domain, TpDebugLevel level, const gchar *message, gpointer user_data, GObject *weak_object) { TpDebugMessage *msg; msg = _tp_debug_message_new (timestamp, domain, level, message); g_signal_emit (self, signals[SIG_NEW_DEBUG_MESSAGE], 0, msg); g_object_unref (msg); } static void tp_debug_client_constructed (GObject *object) { TpDebugClient *self = TP_DEBUG_CLIENT (object); TpProxy *proxy = TP_PROXY (object); GObjectClass *parent_class = G_OBJECT_CLASS (tp_debug_client_parent_class); GError *error = NULL; if (parent_class->constructed != NULL) parent_class->constructed (object); tp_dbus_daemon_watch_name_owner ( tp_proxy_get_dbus_daemon (proxy), tp_proxy_get_bus_name (proxy), name_owner_changed_cb, object, NULL); tp_debug_client_prepare_core (self); if (!tp_cli_debug_connect_to_new_debug_message (self, new_debug_message_cb, NULL, NULL, NULL, &error)) { WARNING ("Failed to connect to NewDebugMessage: %s", error->message); g_error_free (error); } } static void tp_debug_client_dispose (GObject *object) { TpProxy *proxy = TP_PROXY (object); GObjectClass *parent_class = G_OBJECT_CLASS (tp_debug_client_parent_class); tp_dbus_daemon_cancel_name_owner_watch ( tp_proxy_get_dbus_daemon (proxy), tp_proxy_get_bus_name (proxy), name_owner_changed_cb, object); if (parent_class->dispose != NULL) parent_class->dispose (object); } static void tp_debug_client_class_init (TpDebugClientClass *klass) { GObjectClass *object_class = (GObjectClass *) klass; TpProxyClass *proxy_class = (TpProxyClass *) klass; GParamSpec *spec; object_class->get_property = tp_debug_client_get_property; object_class->constructed = tp_debug_client_constructed; object_class->dispose = tp_debug_client_dispose; proxy_class->must_have_unique_name = TRUE; proxy_class->interface = TP_IFACE_QUARK_DEBUG; proxy_class->list_features = tp_debug_client_list_features; /** * TpDebugClient:enabled: * * %TRUE if debug messages are published on the bus. * * This property is meaningless until the * %TP_DEBUG_CLIENT_FEATURE_CORE feature has been prepared. * * Since: 0.19.0 */ spec = g_param_spec_boolean ("enabled", "enabled", "Enabled", FALSE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_ENABLED, spec); /** * TpDebugClient::new-debug-message: * @self: a #TpDebugClient * @message: a #TpDebugMessage * * Emitted when a #TpDebugMessage is generated if the TpDebugMessage:enabled * property is set to %TRUE. * * Since: 0.19.0 */ signals[SIG_NEW_DEBUG_MESSAGE] = g_signal_new ("new-debug-message", G_OBJECT_CLASS_TYPE (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 1, TP_TYPE_DEBUG_MESSAGE); g_type_class_add_private (klass, sizeof (TpDebugClientPrivate)); tp_debug_client_init_known_interfaces (); } GQuark tp_debug_client_get_feature_quark_core (void) { return g_quark_from_static_string ("tp-debug-client-feature-core"); } static void name_owner_changed_cb ( TpDBusDaemon *bus, const gchar *name, const gchar *new_owner, gpointer user_data) { TpDebugClient *self = TP_DEBUG_CLIENT (user_data); if (tp_str_empty (new_owner)) { GError *error = g_error_new (TP_DBUS_ERRORS, TP_DBUS_ERROR_NAME_OWNER_LOST, "%s fell off the bus", name); DEBUG ("%s fell off the bus", name); tp_proxy_invalidate (TP_PROXY (self), error); g_error_free (error); } } static void got_enabled_cb ( TpProxy *proxy, const GValue *value, const GError *error, gpointer user_data, GObject *weak_object) { TpDebugClient *self = TP_DEBUG_CLIENT (proxy); if (error != NULL) { tp_proxy_invalidate (proxy, error); } else if (!G_VALUE_HOLDS_BOOLEAN (value)) { GError *e = g_error_new (TP_ERROR, TP_ERROR_NOT_IMPLEMENTED, "this service doesn't implement the Debug interface correctly " "(the Enabled property is not a boolean, but a %s)", G_VALUE_TYPE_NAME (value)); tp_proxy_invalidate (proxy, e); g_error_free (e); } else { self->priv->enabled = g_value_get_boolean (value); /* FIXME: we have no change notification for Enabled. */ _tp_proxy_set_feature_prepared (proxy, TP_DEBUG_CLIENT_FEATURE_CORE, TRUE); } } static void tp_debug_client_prepare_core (TpDebugClient *self) { tp_cli_dbus_properties_call_get (self, -1, TP_IFACE_DEBUG, "Enabled", got_enabled_cb, NULL, NULL, NULL); } static const TpProxyFeature * tp_debug_client_list_features (TpProxyClass *klass) { static gsize once = 0; static TpProxyFeature features[] = { { 0, TRUE }, { 0 } }; if (g_once_init_enter (&once)) { features[0].name = TP_DEBUG_CLIENT_FEATURE_CORE; g_once_init_leave (&once, 1); } return features; } /** * tp_debug_client_init_known_interfaces: * * Ensure that the known interfaces for TpDebugClient have been set up. * This is done automatically when necessary, but for correct * overriding of library interfaces by local extensions, you should * call this function before calling * tp_proxy_or_subclass_hook_on_interface_add() with first argument * %TP_TYPE_DEBUG_CLIENT. * * Since: 0.19.0 */ void tp_debug_client_init_known_interfaces (void) { static gsize once = 0; if (g_once_init_enter (&once)) { GType tp_type = TP_TYPE_DEBUG_CLIENT; tp_proxy_init_known_interfaces (); tp_proxy_or_subclass_hook_on_interface_add (tp_type, tp_cli_debug_add_signals); tp_proxy_subclass_add_error_mapping (tp_type, TP_ERROR_PREFIX, TP_ERROR, TP_TYPE_ERROR); g_once_init_leave (&once, 1); } } /** * tp_debug_client_new: * @dbus: a D-Bus daemon; may not be %NULL * @unique_name: the unique name of the process to be debugged; may not be * %NULL or a well-known name * @error: used to raise an error if @unique_name is not valid * * * * Returns: a new debug client proxy, or %NULL on invalid arguments * * Since: 0.19.0 */ TpDebugClient * tp_debug_client_new ( TpDBusDaemon *dbus, const gchar *unique_name, GError **error) { if (!tp_dbus_check_valid_bus_name (unique_name, TP_DBUS_NAME_TYPE_UNIQUE, error)) return NULL; return TP_DEBUG_CLIENT (g_object_new (TP_TYPE_DEBUG_CLIENT, "dbus-daemon", dbus, "bus-name", unique_name, "object-path", TP_DEBUG_OBJECT_PATH, NULL)); } static void set_enabled_cb ( TpProxy *proxy, const GError *error, gpointer user_data, GObject *weak_object) { GSimpleAsyncResult *result = G_SIMPLE_ASYNC_RESULT (user_data); if (error != NULL) g_simple_async_result_set_from_error (result, error); g_simple_async_result_complete (result); } /** * tp_debug_client_set_enabled_async: * @self: a #TpDebugClient * @enabled: %TRUE if debug messages should be published on the bus, %FALSE * otherwise * @callback: a callback to call when the request is satisfied * @user_data: data to pass to @callback * * Enable or disable publishing of debug messages on the bus by the component * owning @self's bus name. * * Since: 0.19.0 */ void tp_debug_client_set_enabled_async ( TpDebugClient *self, gboolean enabled, GAsyncReadyCallback callback, gpointer user_data) { GSimpleAsyncResult *result = g_simple_async_result_new (G_OBJECT (self), callback, user_data, tp_debug_client_set_enabled_async); GValue v = { 0, }; g_value_init (&v, G_TYPE_BOOLEAN); g_value_set_boolean (&v, enabled); tp_cli_dbus_properties_call_set (self, -1, TP_IFACE_DEBUG, "Enabled", &v, set_enabled_cb, result, g_object_unref, NULL); g_value_unset (&v); } /** * tp_debug_client_set_enabled_finish: * @self: a #TpDebugClient * @result: a #GAsyncResult * @error: a #GError to fill * * Finishes tp_debug_client_set_enabled_async(). * * Returns: %TRUE, if the operation suceeded, %FALSE otherwise * Since: 0.19.0 */ gboolean tp_debug_client_set_enabled_finish ( TpDebugClient *self, GAsyncResult *result, GError **error) { _tp_implement_finish_void (self, tp_debug_client_set_enabled_async) } /** * tp_debug_client_is_enabled: * @self: a #TpDebugClient * * Return the #TpDebugClient:enabled property * * Returns: the value of #TpDebugClient:enabled property * * Since: 0.19.0 */ gboolean tp_debug_client_is_enabled (TpDebugClient *self) { return self->priv->enabled; } static void get_messages_cb (TpDebugClient *self, const GPtrArray *messages, const GError *error, gpointer user_data, GObject *weak_object) { GSimpleAsyncResult *result = user_data; guint i; GPtrArray *messages_arr; if (error != NULL) { DEBUG ("GetMessages() failed: %s", error->message); g_simple_async_result_set_from_error (result, error); goto out; } messages_arr = g_ptr_array_new_with_free_func (g_object_unref); for (i = 0; i < messages->len; i++) { TpDebugMessage *msg; gdouble timestamp; const gchar *domain, *message; TpDebugLevel level; tp_value_array_unpack (g_ptr_array_index (messages, i), 4, ×tamp, &domain, &level, &message); msg = _tp_debug_message_new (timestamp, domain, level, message); g_ptr_array_add (messages_arr, msg); } g_simple_async_result_set_op_res_gpointer (result, messages_arr, (GDestroyNotify) g_ptr_array_unref); out: g_simple_async_result_complete (result); } /** * tp_debug_client_get_messages_async: * @self: a #TpDebugClient * @callback: callback to call when the messages have been retrieved * @user_data: data to pass to @callback * * Retrieve buffered messages from @self. Once @callback is called, * use tp_debug_client_get_messages_finish() to retrieve the #TpDebugMessage * objects. * * Since: 0.19.0 */ void tp_debug_client_get_messages_async ( TpDebugClient *self, GAsyncReadyCallback callback, gpointer user_data) { GSimpleAsyncResult *result = g_simple_async_result_new (G_OBJECT (self), callback, user_data, tp_debug_client_set_enabled_async); tp_cli_debug_call_get_messages (self, -1, get_messages_cb, result, g_object_unref, NULL); } /** * tp_debug_client_get_messages_finish: * @self: a #TpDebugClient * @result: a #GAsyncResult * @error: a #GError to fill * * Finishes tp_debug_client_set_enabled_async(). * * Returns: (transfer container) (type GLib.PtrArray) (element-type TelepathyGLib.DebugMessage): * a #GPtrArray of #TpDebugMessage, free with g_ptr_array_unref() * * Since: 0.19.0 */ GPtrArray * tp_debug_client_get_messages_finish (TpDebugClient *self, GAsyncResult *result, GError **error) { _tp_implement_finish_return_copy_pointer (self, tp_debug_client_set_enabled_async, g_ptr_array_ref) }