/* -*- mode: C; c-file-style: "gnu" -*- */ /* dbus-gproxy.c Proxy for remote objects * * Copyright (C) 2003, 2004, 2005 Red Hat, Inc. * Copyright (C) 2005 Nokia * * SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later * * Licensed under the Academic Free License version 2.1 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #include #include #include #include #include "dbus-gutils.h" #include "dbus-gsignature.h" #include "dbus-gvalue.h" #include "dbus-gvalue-utils.h" #include "dbus-gobject.h" #include #include #include #define DBUS_G_PROXY_CALL_TO_ID(x) (GPOINTER_TO_UINT(x)) #define DBUS_G_PROXY_ID_TO_CALL(x) (GUINT_TO_POINTER(x)) #define DBUS_G_PROXY_GET_PRIVATE(o) \ (G_TYPE_INSTANCE_GET_PRIVATE ((o), DBUS_TYPE_G_PROXY, DBusGProxyPrivate)) static void oom (void) G_GNUC_NORETURN; static void oom (void) { g_error ("no memory"); } typedef struct _DBusGProxyManager DBusGProxyManager; typedef struct _DBusGProxyPrivate DBusGProxyPrivate; struct _DBusGProxyPrivate { DBusGProxyManager *manager; /**< Proxy manager */ char *name; /**< Name messages go to or NULL */ char *path; /**< Path messages go to or NULL */ char *interface; /**< Interface messages go to or NULL */ DBusGProxyCall *name_call; /**< Pending call id to retrieve name owner */ guint for_owner : 1; /**< Whether or not this proxy is for a name owner */ guint associated : 1; /**< Whether or not this proxy is associated (for name proxies) */ /* FIXME: make threadsafe? */ guint call_id_counter; /**< Integer counter for pending calls */ GData *signal_signatures; /**< D-BUS signatures for each signal */ GHashTable *pending_calls; /**< Calls made on this proxy which have not yet returned */ int default_timeout; /**< Default timeout to use, see dbus_g_proxy_set_default_timeout */ }; static void dbus_g_proxy_init (DBusGProxy *proxy); static void dbus_g_proxy_class_init (DBusGProxyClass *klass); static GObject *dbus_g_proxy_constructor (GType type, guint n_construct_properties, GObjectConstructParam *construct_properties); static void dbus_g_proxy_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec); static void dbus_g_proxy_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec); static void dbus_g_proxy_finalize (GObject *object); static void dbus_g_proxy_dispose (GObject *object); static void dbus_g_proxy_destroy (DBusGProxy *proxy); static void dbus_g_proxy_emit_remote_signal (DBusGProxy *proxy, DBusMessage *message); static DBusGProxyCall *manager_begin_bus_call (DBusGProxyManager *manager, const char *method, DBusGProxyCallNotify notify, gpointer data, GDestroyNotify destroy, GType first_arg_type, ...); static guint dbus_g_proxy_begin_call_internal (DBusGProxy *proxy, const char *method, DBusGProxyCallNotify notify, gpointer data, GDestroyNotify destroy, GValueArray *args, int timeout ); static gboolean dbus_g_proxy_end_call_internal (DBusGProxy *proxy, guint call_id, GError **error, GType first_arg_type, va_list args); /* * A list of proxies with a given name+path+interface, used to * route incoming signals. */ typedef struct { GSList *proxies; /**< The list of proxies */ char name[4]; /**< name (empty string for none), nul byte, * path, nul byte, * interface, nul byte */ } DBusGProxyList; /* * DBusGProxyManager's primary task is to route signals to the proxies * those signals are emitted on. In order to do this it also has to * track the owners of the names proxies are bound to. */ struct _DBusGProxyManager { GStaticMutex lock; /**< Thread lock */ int refcount; /**< Reference count */ DBusConnection *connection; /**< Connection we're associated with. */ DBusGProxy *bus_proxy; /**< Special internal proxy used to talk to the bus */ GHashTable *proxy_lists; /**< Hash used to route incoming signals * and iterate over proxies * tristring -> DBusGProxyList */ GHashTable *owner_match_rules; /**< Hash to keep track of match rules of * NameOwnerChanged. * gchar *name -> guint *refcount */ GHashTable *owner_names; /**< Hash to keep track of mapping from * char * -> GSList of DBusGProxyNameOwnerInfo * base name -> [name,name,...] for proxies which * are for names. */ GSList *unassociated_proxies; /**< List of name proxies for which * there was no result for * GetNameOwner */ }; static DBusGProxyManager *dbus_g_proxy_manager_ref (DBusGProxyManager *manager); static DBusHandlerResult dbus_g_proxy_manager_filter (DBusConnection *connection, DBusMessage *message, void *user_data); /** Lock the DBusGProxyManager */ #define LOCK_MANAGER(mgr) (g_static_mutex_lock (&(mgr)->lock)) /** Unlock the DBusGProxyManager */ #define UNLOCK_MANAGER(mgr) (g_static_mutex_unlock (&(mgr)->lock)) static int g_proxy_manager_slot = -1; /* Lock controlling get/set manager as data on each connection */ static GStaticMutex connection_g_proxy_lock = G_STATIC_MUTEX_INIT; static DBusGProxyManager* dbus_g_proxy_manager_get (DBusConnection *connection) { DBusGProxyManager *manager; dbus_connection_allocate_data_slot (&g_proxy_manager_slot); if (g_proxy_manager_slot < 0) g_error ("out of memory"); g_static_mutex_lock (&connection_g_proxy_lock); manager = dbus_connection_get_data (connection, g_proxy_manager_slot); if (manager != NULL) { dbus_connection_free_data_slot (&g_proxy_manager_slot); dbus_g_proxy_manager_ref (manager); g_static_mutex_unlock (&connection_g_proxy_lock); return manager; } manager = g_new0 (DBusGProxyManager, 1); manager->refcount = 1; manager->connection = connection; g_static_mutex_init (&manager->lock); /* Proxy managers keep the connection alive, which means that * DBusGProxy indirectly does. To free a connection you have to free * all the proxies referring to it. */ dbus_connection_ref (manager->connection); dbus_connection_set_data (connection, g_proxy_manager_slot, manager, NULL); dbus_connection_add_filter (connection, dbus_g_proxy_manager_filter, manager, NULL); g_static_mutex_unlock (&connection_g_proxy_lock); return manager; } static DBusGProxyManager * dbus_g_proxy_manager_ref (DBusGProxyManager *manager) { g_assert (manager != NULL); g_assert (manager->refcount > 0); LOCK_MANAGER (manager); manager->refcount += 1; UNLOCK_MANAGER (manager); return manager; } static void dbus_g_proxy_manager_unref (DBusGProxyManager *manager) { g_assert (manager != NULL); g_assert (manager->refcount > 0); LOCK_MANAGER (manager); manager->refcount -= 1; if (manager->refcount == 0) { UNLOCK_MANAGER (manager); if (manager->bus_proxy) g_object_unref (manager->bus_proxy); if (manager->proxy_lists) { /* can't have any proxies left since they hold * a reference to the proxy manager. */ g_assert (g_hash_table_size (manager->proxy_lists) == 0); g_hash_table_destroy (manager->proxy_lists); manager->proxy_lists = NULL; } if (manager->owner_match_rules) { /* Since we destroyed all proxies, none can be tracking * name owners */ g_assert (g_hash_table_size (manager->owner_match_rules) == 0); g_hash_table_destroy (manager->owner_match_rules); manager->owner_match_rules = NULL; } if (manager->owner_names) { /* Since we destroyed all proxies, none can be tracking * name owners */ g_assert (g_hash_table_size (manager->owner_names) == 0); g_hash_table_destroy (manager->owner_names); manager->owner_names = NULL; } g_assert (manager->unassociated_proxies == NULL); g_static_mutex_free (&manager->lock); g_static_mutex_lock (&connection_g_proxy_lock); dbus_connection_remove_filter (manager->connection, dbus_g_proxy_manager_filter, manager); dbus_connection_set_data (manager->connection, g_proxy_manager_slot, NULL, NULL); g_static_mutex_unlock (&connection_g_proxy_lock); dbus_connection_unref (manager->connection); g_free (manager); dbus_connection_free_data_slot (&g_proxy_manager_slot); } else { UNLOCK_MANAGER (manager); } } static guint tristring_hash (gconstpointer key) { const char *p = key; guint h = *p; if (h) { for (p += 1; *p != '\0'; p++) h = (h << 5) - h + *p; } /* skip nul and do the next substring */ for (p += 1; *p != '\0'; p++) h = (h << 5) - h + *p; /* skip nul again and another substring */ for (p += 1; *p != '\0'; p++) h = (h << 5) - h + *p; return h; } static gboolean strequal_len (const char *a, const char *b, size_t *lenp) { size_t a_len; size_t b_len; a_len = strlen (a); b_len = strlen (b); if (a_len != b_len) return FALSE; if (memcmp (a, b, a_len) != 0) return FALSE; *lenp = a_len; return TRUE; } static gboolean tristring_equal (gconstpointer a, gconstpointer b) { const char *ap = a; const char *bp = b; size_t len; if (!strequal_len (ap, bp, &len)) return FALSE; ap += len + 1; bp += len + 1; if (!strequal_len (ap, bp, &len)) return FALSE; ap += len + 1; bp += len + 1; if (strcmp (ap, bp) != 0) return FALSE; return TRUE; } static char* tristring_alloc_from_strings (size_t padding_before, const char *name, const char *path, const char *interface) { size_t name_len, iface_len, path_len, len; char *tri; if (name) name_len = strlen (name); else name_len = 0; path_len = strlen (path); iface_len = strlen (interface); tri = g_malloc (padding_before + name_len + path_len + iface_len + 3); len = padding_before; if (name) memcpy (&tri[len], name, name_len); len += name_len; tri[len] = '\0'; len += 1; g_assert (len == (padding_before + name_len + 1)); memcpy (&tri[len], path, path_len); len += path_len; tri[len] = '\0'; len += 1; g_assert (len == (padding_before + name_len + path_len + 2)); memcpy (&tri[len], interface, iface_len); len += iface_len; tri[len] = '\0'; len += 1; g_assert (len == (padding_before + name_len + path_len + iface_len + 3)); return tri; } static char* tristring_from_proxy (DBusGProxy *proxy) { DBusGProxyPrivate *priv = DBUS_G_PROXY_GET_PRIVATE(proxy); return tristring_alloc_from_strings (0, priv->name, priv->path, priv->interface); } static char* tristring_from_message (DBusMessage *message) { const char *path; const char *interface; path = dbus_message_get_path (message); interface = dbus_message_get_interface (message); g_assert (path); g_assert (interface); return tristring_alloc_from_strings (0, dbus_message_get_sender (message), path, interface); } static DBusGProxyList* g_proxy_list_new (DBusGProxy *first_proxy) { DBusGProxyList *list; DBusGProxyPrivate *priv = DBUS_G_PROXY_GET_PRIVATE(first_proxy); list = (void*) tristring_alloc_from_strings (G_STRUCT_OFFSET (DBusGProxyList, name), priv->name, priv->path, priv->interface); list->proxies = NULL; return list; } static void g_proxy_list_free (DBusGProxyList *list) { /* we don't hold a reference to the proxies in the list, * as they ref the GProxyManager */ g_slist_free (list->proxies); g_free (list); } static char* g_proxy_get_signal_match_rule (DBusGProxy *proxy) { DBusGProxyPrivate *priv = DBUS_G_PROXY_GET_PRIVATE(proxy); /* FIXME Escaping is required here */ if (priv->name) return g_strdup_printf ("type='signal',sender='%s',path='%s',interface='%s'", priv->name, priv->path, priv->interface); else return g_strdup_printf ("type='signal',path='%s',interface='%s'", priv->path, priv->interface); } static char * get_owner_match_rule (const gchar *name) { return g_strdup_printf ("type='signal',sender='" DBUS_SERVICE_DBUS "',path='" DBUS_PATH_DBUS "',interface='" DBUS_INTERFACE_DBUS "',member='NameOwnerChanged'" ",arg0='%s'", name); } typedef struct { char *name; guint refcount; } DBusGProxyNameOwnerInfo; static gint find_name_in_info (gconstpointer a, gconstpointer b) { const DBusGProxyNameOwnerInfo *info = a; const char *name = b; return strcmp (info->name, name); } typedef struct { const char *name; const char *owner; DBusGProxyNameOwnerInfo *info; } DBusGProxyNameOwnerForeachData; static void name_owner_foreach (gpointer key, gpointer val, gpointer data) { const char *owner; DBusGProxyNameOwnerForeachData *foreach_data; GSList *names; GSList *link; owner = key; names = val; foreach_data = data; if (foreach_data->owner != NULL) return; g_assert (foreach_data->info == NULL); link = g_slist_find_custom (names, foreach_data->name, find_name_in_info); if (link) { foreach_data->owner = owner; foreach_data->info = link->data; } } static gboolean dbus_g_proxy_manager_lookup_name_owner (DBusGProxyManager *manager, const char *name, DBusGProxyNameOwnerInfo **info, const char **owner) { DBusGProxyNameOwnerForeachData foreach_data; foreach_data.name = name; foreach_data.owner = NULL; foreach_data.info = NULL; g_hash_table_foreach (manager->owner_names, name_owner_foreach, &foreach_data); *info = foreach_data.info; *owner = foreach_data.owner; return *info != NULL; } static void insert_nameinfo (DBusGProxyManager *manager, const char *owner, DBusGProxyNameOwnerInfo *info) { GSList *names; gboolean insert; names = g_hash_table_lookup (manager->owner_names, owner); /* Only need to g_hash_table_insert the first time */ insert = (names == NULL); names = g_slist_append (names, info); if (insert) g_hash_table_insert (manager->owner_names, g_strdup (owner), names); } static void dbus_g_proxy_manager_monitor_name_owner (DBusGProxyManager *manager, const char *owner, const char *name) { GSList *names; GSList *link; DBusGProxyNameOwnerInfo *nameinfo; names = g_hash_table_lookup (manager->owner_names, owner); link = g_slist_find_custom (names, name, find_name_in_info); if (!link) { nameinfo = g_new0 (DBusGProxyNameOwnerInfo, 1); nameinfo->name = g_strdup (name); nameinfo->refcount = 1; insert_nameinfo (manager, owner, nameinfo); } else { nameinfo = link->data; nameinfo->refcount++; } } static void dbus_g_proxy_manager_unmonitor_name_owner (DBusGProxyManager *manager, const char *name) { DBusGProxyNameOwnerInfo *info; const char *owner; gboolean ret; ret = dbus_g_proxy_manager_lookup_name_owner (manager, name, &info, &owner); g_assert (ret); g_assert (info != NULL); g_assert (owner != NULL); info->refcount--; if (info->refcount == 0) { GSList *names; GSList *link; names = g_hash_table_lookup (manager->owner_names, owner); link = g_slist_find_custom (names, name, find_name_in_info); names = g_slist_delete_link (names, link); if (names != NULL) g_hash_table_insert (manager->owner_names, g_strdup (owner), names); else g_hash_table_remove (manager->owner_names, owner); g_free (info->name); g_free (info); } } typedef struct { const char *name; GSList *destroyed; } DBusGProxyUnassociateData; static void unassociate_proxies (gpointer key, gpointer val, gpointer user_data) { DBusGProxyList *list; const char *name; GSList *tmp; DBusGProxyUnassociateData *data; list = val; data = user_data; name = data->name; for (tmp = list->proxies; tmp; tmp = tmp->next) { DBusGProxy *proxy = DBUS_G_PROXY (tmp->data); DBusGProxyPrivate *priv = DBUS_G_PROXY_GET_PRIVATE(proxy); DBusGProxyManager *manager; manager = priv->manager; if (priv->name != NULL && !strcmp (priv->name, name)) { if (!priv->for_owner) { /* If a service appeared and then vanished very quickly, * it's conceivable we have an inflight request for * GetNameOwner here. Cancel it. * https://bugs.freedesktop.org/show_bug.cgi?id=18573 */ if (priv->name_call) dbus_g_proxy_cancel_call (manager->bus_proxy, priv->name_call); priv->name_call = NULL; priv->associated = FALSE; manager->unassociated_proxies = g_slist_prepend (manager->unassociated_proxies, proxy); } else { data->destroyed = g_slist_prepend (data->destroyed, proxy); /* make contents of list into weak pointers in case the objects * unref each other when disposing */ g_object_add_weak_pointer (G_OBJECT (proxy), &(data->destroyed->data)); } } } } static void dbus_g_proxy_manager_replace_name_owner (DBusGProxyManager *manager, const char *name, const char *prev_owner, const char *new_owner) { GSList *names; if (prev_owner[0] == '\0') { GSList *tmp; GSList *removed; /* We have a new service, look at unassociated proxies */ removed = NULL; for (tmp = manager->unassociated_proxies; tmp ; tmp = tmp->next) { DBusGProxy *proxy = tmp->data; DBusGProxyPrivate *priv = DBUS_G_PROXY_GET_PRIVATE(proxy); if (!strcmp (priv->name, name)) { removed = g_slist_prepend (removed, tmp); dbus_g_proxy_manager_monitor_name_owner (manager, new_owner, name); priv->associated = TRUE; } } for (tmp = removed; tmp; tmp = tmp->next) manager->unassociated_proxies = g_slist_delete_link (manager->unassociated_proxies, tmp->data); g_slist_free (removed); } else { DBusGProxyNameOwnerInfo *info; GSList *link; /* Name owner changed or deleted */ names = g_hash_table_lookup (manager->owner_names, prev_owner); info = NULL; if (names != NULL) { link = g_slist_find_custom (names, name, find_name_in_info); if (link != NULL) { info = link->data; names = g_slist_delete_link (names, link); if (names == NULL) { g_hash_table_remove (manager->owner_names, prev_owner); } else { g_hash_table_insert (manager->owner_names, g_strdup (prev_owner), names); } } } if (new_owner[0] == '\0') { DBusGProxyUnassociateData data; GSList *tmp; data.name = name; data.destroyed = NULL; /* A service went away, we need to unassociate proxies */ g_hash_table_foreach (manager->proxy_lists, unassociate_proxies, &data); UNLOCK_MANAGER (manager); /* the destroyed list's data pointers are weak pointers, so that we * don't end up calling destroy on proxies which have already been * freed up as a result of other ones being destroyed */ for (tmp = data.destroyed; tmp; tmp = tmp->next) if (tmp->data != NULL) { g_object_remove_weak_pointer (G_OBJECT (tmp->data), &(tmp->data)); dbus_g_proxy_destroy (tmp->data); } g_slist_free (data.destroyed); LOCK_MANAGER (manager); if (info) { g_free (info->name); g_free (info); } } else if (info) { insert_nameinfo (manager, new_owner, info); } } } static void got_name_owner_cb (DBusGProxy *bus_proxy, DBusGProxyCall *call, void *user_data) { DBusGProxy *proxy = user_data; DBusGProxyPrivate *priv = DBUS_G_PROXY_GET_PRIVATE(proxy); GError *error; char *owner; error = NULL; owner = NULL; LOCK_MANAGER (priv->manager); if (!dbus_g_proxy_end_call (bus_proxy, call, &error, G_TYPE_STRING, &owner, G_TYPE_INVALID)) { if (error->domain == DBUS_GERROR && error->code == DBUS_GERROR_NAME_HAS_NO_OWNER) { priv->manager->unassociated_proxies = g_slist_prepend (priv->manager->unassociated_proxies, proxy); } else if (error->domain == DBUS_GERROR && error->code == DBUS_GERROR_REMOTE_EXCEPTION) g_warning ("Couldn't get name owner (%s): %s", dbus_g_error_get_name (error), error->message); else g_warning ("Couldn't get name owner (code %d): %s", error->code, error->message); g_clear_error (&error); goto out; } else { dbus_g_proxy_manager_monitor_name_owner (priv->manager, owner, priv->name); priv->associated = TRUE; } out: priv->name_call = NULL; UNLOCK_MANAGER (priv->manager); g_free (owner); } static char * get_name_owner (DBusConnection *connection, const char *name, GError **error) { DBusError derror; DBusMessage *request, *reply; char *base_name; dbus_error_init (&derror); base_name = NULL; reply = NULL; request = dbus_message_new_method_call (DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS, "GetNameOwner"); if (request == NULL) g_error ("Out of memory"); if (!dbus_message_append_args (request, DBUS_TYPE_STRING, &name, DBUS_TYPE_INVALID)) g_error ("Out of memory"); reply = dbus_connection_send_with_reply_and_block (connection, request, 2000, &derror); if (reply == NULL) goto error; if (dbus_set_error_from_message (&derror, reply)) goto error; if (!dbus_message_get_args (reply, &derror, DBUS_TYPE_STRING, &base_name, DBUS_TYPE_INVALID)) goto error; base_name = g_strdup (base_name); goto out; error: g_assert (dbus_error_is_set (&derror)); dbus_set_g_error (error, &derror); dbus_error_free (&derror); out: if (request) dbus_message_unref (request); if (reply) dbus_message_unref (reply); return base_name; } static void guint_slice_free (gpointer data) { g_slice_free (guint, data); } static void dbus_g_proxy_manager_register (DBusGProxyManager *manager, DBusGProxy *proxy) { DBusGProxyList *list; DBusGProxyPrivate *priv = DBUS_G_PROXY_GET_PRIVATE(proxy); LOCK_MANAGER (manager); if (manager->proxy_lists == NULL) { g_assert (manager->owner_names == NULL); g_assert (manager->owner_match_rules == NULL); list = NULL; manager->proxy_lists = g_hash_table_new_full (tristring_hash, tristring_equal, NULL, (GFreeFunc) g_proxy_list_free); manager->owner_names = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); manager->owner_match_rules = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, guint_slice_free); } else { char *tri; tri = tristring_from_proxy (proxy); list = g_hash_table_lookup (manager->proxy_lists, tri); g_free (tri); } if (list == NULL) { list = g_proxy_list_new (proxy); g_hash_table_replace (manager->proxy_lists, list->name, list); } if (list->proxies == NULL && priv->name) { /* We have to add match rules to the server, * but only if the server is a message bus, * not if it's a peer. */ char *rule; guint *refcount; rule = g_proxy_get_signal_match_rule (proxy); /* We don't check for errors; it's not like anyone would handle them, and * we don't want a round trip here. */ dbus_bus_add_match (manager->connection, rule, NULL); g_free (rule); refcount = g_hash_table_lookup (manager->owner_match_rules, priv->name); if (refcount != NULL) { g_assert (*refcount != 0); g_assert (*refcount < G_MAXUINT); (*refcount)++; } else { char *rule; rule = get_owner_match_rule (priv->name); dbus_bus_add_match (manager->connection, rule, NULL); g_free (rule); refcount = g_slice_new (guint); *refcount = 1; g_hash_table_insert (manager->owner_match_rules, g_strdup (priv->name), refcount); } } g_assert (g_slist_find (list->proxies, proxy) == NULL); list->proxies = g_slist_prepend (list->proxies, proxy); if (!priv->for_owner) { const char *owner; DBusGProxyNameOwnerInfo *info; if (!dbus_g_proxy_manager_lookup_name_owner (manager, priv->name, &info, &owner)) { priv->name_call = manager_begin_bus_call (manager, "GetNameOwner", got_name_owner_cb, proxy, NULL, G_TYPE_STRING, priv->name, G_TYPE_INVALID); priv->associated = FALSE; } else { info->refcount++; priv->associated = TRUE; } } UNLOCK_MANAGER (manager); } static void dbus_g_proxy_manager_unregister (DBusGProxyManager *manager, DBusGProxy *proxy) { DBusGProxyList *list; DBusGProxyPrivate *priv = DBUS_G_PROXY_GET_PRIVATE(proxy); char *tri; LOCK_MANAGER (manager); #ifndef G_DISABLE_CHECKS if (manager->proxy_lists == NULL) { g_warning ("Trying to unregister a proxy but there aren't any registered"); return; } #endif tri = tristring_from_proxy (proxy); list = g_hash_table_lookup (manager->proxy_lists, tri); #ifndef G_DISABLE_CHECKS if (list == NULL) { g_warning ("Trying to unregister a proxy but it isn't registered"); return; } #endif g_assert (g_slist_find (list->proxies, proxy) != NULL); list->proxies = g_slist_remove (list->proxies, proxy); g_assert (g_slist_find (list->proxies, proxy) == NULL); if (!priv->for_owner) { if (!priv->associated) { GSList *link; if (priv->name_call != 0) { dbus_g_proxy_cancel_call (manager->bus_proxy, priv->name_call); priv->name_call = 0; } else { link = g_slist_find (manager->unassociated_proxies, proxy); if (link != NULL) { manager->unassociated_proxies = g_slist_delete_link ( manager->unassociated_proxies, link); } } } else { g_assert (priv->name_call == 0); dbus_g_proxy_manager_unmonitor_name_owner (manager, priv->name); } } if (list->proxies == NULL) { char *rule; g_hash_table_remove (manager->proxy_lists, tri); rule = g_proxy_get_signal_match_rule (proxy); dbus_bus_remove_match (manager->connection, rule, NULL); g_free (rule); if (priv->name) { guint *refcount; refcount = g_hash_table_lookup (manager->owner_match_rules, priv->name); (*refcount)--; if (*refcount == 0) { rule = get_owner_match_rule (priv->name); dbus_bus_remove_match (manager->connection, rule, NULL); g_free (rule); g_hash_table_remove (manager->owner_match_rules, priv->name); } } } if (g_hash_table_size (manager->proxy_lists) == 0) { g_hash_table_destroy (manager->proxy_lists); manager->proxy_lists = NULL; } if (manager->owner_match_rules != NULL && g_hash_table_size (manager->owner_match_rules) == 0) { g_hash_table_destroy (manager->owner_match_rules); manager->owner_match_rules = NULL; } g_free (tri); UNLOCK_MANAGER (manager); } static void list_proxies_foreach (gpointer key, gpointer value, gpointer user_data) { DBusGProxyList *list; GSList **ret; GSList *tmp; list = value; ret = user_data; tmp = list->proxies; while (tmp != NULL) { DBusGProxy *proxy = DBUS_G_PROXY (tmp->data); g_object_ref (proxy); *ret = g_slist_prepend (*ret, proxy); tmp = tmp->next; } } static GSList* dbus_g_proxy_manager_list_all (DBusGProxyManager *manager) { GSList *ret; ret = NULL; if (manager->proxy_lists) { g_hash_table_foreach (manager->proxy_lists, list_proxies_foreach, &ret); } return ret; } static DBusHandlerResult dbus_g_proxy_manager_filter (DBusConnection *connection, DBusMessage *message, void *user_data) { DBusGProxyManager *manager; if (dbus_message_get_type (message) != DBUS_MESSAGE_TYPE_SIGNAL) return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; manager = user_data; dbus_g_proxy_manager_ref (manager); LOCK_MANAGER (manager); if (dbus_message_is_signal (message, DBUS_INTERFACE_LOCAL, "Disconnected")) { /* Destroy all the proxies, quite possibly resulting in unreferencing * the proxy manager and the connection as well. */ GSList *all; GSList *tmp; all = dbus_g_proxy_manager_list_all (manager); tmp = all; while (tmp != NULL) { DBusGProxy *proxy; proxy = DBUS_G_PROXY (tmp->data); UNLOCK_MANAGER (manager); dbus_g_proxy_destroy (proxy); g_object_unref (G_OBJECT (proxy)); LOCK_MANAGER (manager); tmp = tmp->next; } g_slist_free (all); #ifndef G_DISABLE_CHECKS if (manager->proxy_lists != NULL) g_warning ("Disconnection emitted \"destroy\" on all DBusGProxy, but somehow new proxies were created in response to one of those destroy signals. This will cause a memory leak."); #endif } else { char *tri; GSList *full_list; GSList *owned_names; GSList *tmp; const char *sender; sender = dbus_message_get_sender (message); /* First we handle NameOwnerChanged internally */ if (g_strcmp0 (sender, DBUS_SERVICE_DBUS) == 0 && dbus_message_is_signal (message, DBUS_INTERFACE_DBUS, "NameOwnerChanged")) { const char *name; const char *prev_owner; const char *new_owner; DBusError derr; dbus_error_init (&derr); if (!dbus_message_get_args (message, &derr, DBUS_TYPE_STRING, &name, DBUS_TYPE_STRING, &prev_owner, DBUS_TYPE_STRING, &new_owner, DBUS_TYPE_INVALID)) { /* Ignore this error */ dbus_error_free (&derr); } else if (manager->owner_names != NULL) { dbus_g_proxy_manager_replace_name_owner (manager, name, prev_owner, new_owner); } } /* dbus spec requires these, libdbus validates */ g_assert (dbus_message_get_path (message) != NULL); g_assert (dbus_message_get_interface (message) != NULL); g_assert (dbus_message_get_member (message) != NULL); tri = tristring_from_message (message); if (manager->proxy_lists) { DBusGProxyList *owner_list; owner_list = g_hash_table_lookup (manager->proxy_lists, tri); if (owner_list) full_list = g_slist_copy (owner_list->proxies); else full_list = NULL; } else full_list = NULL; g_free (tri); if (manager->owner_names && sender) { owned_names = g_hash_table_lookup (manager->owner_names, sender); for (tmp = owned_names; tmp; tmp = tmp->next) { DBusGProxyList *owner_list; DBusGProxyNameOwnerInfo *nameinfo; nameinfo = tmp->data; g_assert (nameinfo->refcount > 0); tri = tristring_alloc_from_strings (0, nameinfo->name, dbus_message_get_path (message), dbus_message_get_interface (message)); owner_list = g_hash_table_lookup (manager->proxy_lists, tri); if (owner_list != NULL) { GSList *elt; /* Ignore duplicates when adding to full_list */ for (elt = owner_list->proxies; elt; elt = g_slist_next (elt)) { if (!g_slist_find (full_list, elt->data)) full_list = g_slist_append (full_list, elt->data); } } g_free (tri); } } #if 0 g_print ("proxy got %s,%s,%s = list %p\n", tri, tri + strlen (tri) + 1, tri + strlen (tri) + 1 + strlen (tri + strlen (tri) + 1) + 1, list); #endif /* Emit the signal */ g_slist_foreach (full_list, (GFunc) g_object_ref, NULL); for (tmp = full_list; tmp; tmp = tmp->next) { DBusGProxy *proxy; proxy = DBUS_G_PROXY (tmp->data); UNLOCK_MANAGER (manager); dbus_g_proxy_emit_remote_signal (proxy, message); g_object_unref (G_OBJECT (proxy)); LOCK_MANAGER (manager); } g_slist_free (full_list); } UNLOCK_MANAGER (manager); dbus_g_proxy_manager_unref (manager); /* "Handling" signals doesn't make sense, they are for everyone * who cares */ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } /* ---------- DBusGProxy -------------- */ #define DBUS_G_PROXY_DESTROYED(proxy) (DBUS_G_PROXY_GET_PRIVATE(proxy)->manager == NULL) static void marshal_dbus_message_to_g_marshaller (GClosure *closure, GValue *return_value, guint n_param_values, const GValue *param_values, gpointer invocation_hint, gpointer marshal_data); enum { PROP_0, PROP_NAME, PROP_PATH, PROP_INTERFACE, PROP_CONNECTION }; enum { DESTROY, RECEIVED, LAST_SIGNAL }; static void *parent_class; static guint signals[LAST_SIGNAL] = { 0 }; static void dbus_g_proxy_init (DBusGProxy *proxy) { DBusGProxyPrivate *priv = DBUS_G_PROXY_GET_PRIVATE(proxy); g_datalist_init (&priv->signal_signatures); priv->pending_calls = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify) dbus_pending_call_unref); priv->name_call = 0; priv->associated = FALSE; priv->default_timeout = -1; } static GObject * dbus_g_proxy_constructor (GType type, guint n_construct_properties, GObjectConstructParam *construct_properties) { DBusGProxy *proxy; DBusGProxyClass *klass; GObjectClass *parent_class; DBusGProxyPrivate *priv; klass = DBUS_G_PROXY_CLASS (g_type_class_peek (DBUS_TYPE_G_PROXY)); parent_class = G_OBJECT_CLASS (g_type_class_peek_parent (klass)); proxy = DBUS_G_PROXY (parent_class->constructor (type, n_construct_properties, construct_properties)); priv = DBUS_G_PROXY_GET_PRIVATE (proxy); /* if these assertions fail, a deriving class has not set our required * parameters - our own public constructors do return_if_fail checks * on these parameters being provided. unfortunately we can't assert * for manager because it's allowed to be NULL when tha mangager is * setting up a bus proxy for its own calls */ g_assert (priv->path != NULL); g_assert (priv->interface != NULL); if (priv->manager != NULL) { dbus_g_proxy_manager_register (priv->manager, proxy); } return G_OBJECT (proxy); } static void dbus_g_proxy_class_init (DBusGProxyClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); parent_class = g_type_class_peek_parent (klass); g_type_class_add_private (klass, sizeof (DBusGProxyPrivate)); object_class->set_property = dbus_g_proxy_set_property; object_class->get_property = dbus_g_proxy_get_property; g_object_class_install_property (object_class, PROP_NAME, g_param_spec_string ("name", "name", "name", NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (object_class, PROP_PATH, g_param_spec_string ("path", "path", "path", NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (object_class, PROP_INTERFACE, g_param_spec_string ("interface", "interface", "interface", NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (object_class, PROP_CONNECTION, g_param_spec_boxed ("connection", "connection", "connection", DBUS_TYPE_G_CONNECTION, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); object_class->finalize = dbus_g_proxy_finalize; object_class->dispose = dbus_g_proxy_dispose; object_class->constructor = dbus_g_proxy_constructor; /** * DBusGProxy::destroy: * @dbusgproxy: the object which received the signal. * * Deprecated: New code should use #GDBusProxy instead. */ signals[DESTROY] = g_signal_new ("destroy", G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_CLEANUP | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS, 0, NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); /* * DBusGProxy::received: * @dbusgproxy: the object which received the signal. * * Deprecated: New code should use #GDBusProxy instead. */ signals[RECEIVED] = g_signal_new ("received", G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, 0, NULL, NULL, marshal_dbus_message_to_g_marshaller, G_TYPE_NONE, 2, DBUS_TYPE_MESSAGE, G_TYPE_POINTER); } static gboolean cancel_pending_call (gpointer key, gpointer val, gpointer data) { DBusPendingCall *pending = val; dbus_pending_call_cancel (pending); return TRUE; } static void dbus_g_proxy_dispose (GObject *object) { DBusGProxy *proxy = DBUS_G_PROXY (object); DBusGProxyPrivate *priv = DBUS_G_PROXY_GET_PRIVATE(proxy); if (priv->pending_calls == NULL) { return; } /* Cancel outgoing pending calls */ g_hash_table_foreach_remove (priv->pending_calls, cancel_pending_call, NULL); g_hash_table_destroy (priv->pending_calls); priv->pending_calls = NULL; if (priv->manager && proxy != priv->manager->bus_proxy) { dbus_g_proxy_manager_unregister (priv->manager, proxy); dbus_g_proxy_manager_unref (priv->manager); } priv->manager = NULL; g_datalist_clear (&priv->signal_signatures); g_signal_emit (object, signals[DESTROY], 0); G_OBJECT_CLASS (parent_class)->dispose (object); } static void dbus_g_proxy_finalize (GObject *object) { DBusGProxy *proxy = DBUS_G_PROXY (object); DBusGProxyPrivate *priv = DBUS_G_PROXY_GET_PRIVATE(proxy); g_return_if_fail (DBUS_G_PROXY_DESTROYED (proxy)); g_free (priv->name); g_free (priv->path); g_free (priv->interface); G_OBJECT_CLASS (parent_class)->finalize (object); } static void dbus_g_proxy_destroy (DBusGProxy *proxy) { /* FIXME do we need the GTK_IN_DESTRUCTION style flag * from GtkObject? */ g_object_run_dispose (G_OBJECT (proxy)); } static void dbus_g_proxy_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { DBusGProxy *proxy = DBUS_G_PROXY (object); DBusGProxyPrivate *priv = DBUS_G_PROXY_GET_PRIVATE(proxy); DBusGConnection *connection; switch (prop_id) { case PROP_NAME: priv->name = g_strdup (g_value_get_string (value)); if (priv->name) priv->for_owner = (priv->name[0] == ':'); else priv->for_owner = TRUE; break; case PROP_PATH: priv->path = g_strdup (g_value_get_string (value)); break; case PROP_INTERFACE: priv->interface = g_strdup (g_value_get_string (value)); break; case PROP_CONNECTION: connection = g_value_get_boxed (value); if (connection != NULL) { priv->manager = dbus_g_proxy_manager_get (DBUS_CONNECTION_FROM_G_CONNECTION (connection)); } break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void dbus_g_proxy_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { DBusGProxy *proxy = DBUS_G_PROXY (object); DBusGProxyPrivate *priv = DBUS_G_PROXY_GET_PRIVATE(proxy); switch (prop_id) { case PROP_NAME: g_value_set_string (value, priv->name); break; case PROP_PATH: g_value_set_string (value, priv->path); break; case PROP_INTERFACE: g_value_set_string (value, priv->interface); break; case PROP_CONNECTION: g_value_set_boxed (value, DBUS_G_CONNECTION_FROM_CONNECTION(priv->manager->connection)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } /* this is to avoid people using g_signal_connect() directly, * to avoid confusion with local signal names, and because * of the horribly broken current setup (signals are added * globally to all proxies) */ static char* create_signal_name (const char *interface, const char *signal) { GString *str; char *p; str = g_string_new (interface); g_string_append (str, "-"); g_string_append (str, signal); /* GLib will silently barf on '.' in signal names */ p = str->str; while (*p) { if (*p == '.') *p = '-'; ++p; } return g_string_free (str, FALSE); } static void marshal_dbus_message_to_g_marshaller (GClosure *closure, GValue *return_value, guint n_param_values, const GValue *param_values, gpointer invocation_hint, gpointer marshal_data) { /* Incoming here we have three params, the instance (Proxy), the * DBusMessage, the signature. We want to convert that to an * expanded GValue array, then call an appropriate normal GLib * marshaller. */ #define MAX_SIGNATURE_ARGS 20 GValueArray *value_array; DBusGProxy *proxy; DBusMessage *message; GArray *gsignature; const GType *types; DBusGProxyPrivate *priv; g_assert (n_param_values == 3); proxy = g_value_get_object (¶m_values[0]); message = g_value_get_boxed (¶m_values[1]); gsignature = g_value_get_pointer (¶m_values[2]); g_return_if_fail (DBUS_IS_G_PROXY (proxy)); g_return_if_fail (message != NULL); g_return_if_fail (gsignature != NULL); priv = DBUS_G_PROXY_GET_PRIVATE(proxy); { DBusGValueMarshalCtx context; context.recursion_depth = 0; context.gconnection = DBUS_G_CONNECTION_FROM_CONNECTION (priv->manager->connection); context.proxy = proxy; types = (const GType*) gsignature->data; value_array = _dbus_gvalue_demarshal_message (&context, message, gsignature->len, types, NULL); } if (value_array == NULL) return; g_value_array_prepend (value_array, NULL); g_value_init (g_value_array_get_nth (value_array, 0), G_TYPE_FROM_INSTANCE (proxy)); g_value_set_instance (g_value_array_get_nth (value_array, 0), proxy); g_cclosure_marshal_generic (closure, return_value, value_array->n_values, value_array->values, invocation_hint, marshal_data); g_value_array_free (value_array); } static void dbus_g_proxy_emit_remote_signal (DBusGProxy *proxy, DBusMessage *message) { const char *interface; const char *signal; char *name; GQuark q; DBusGProxyPrivate *priv = DBUS_G_PROXY_GET_PRIVATE(proxy); GArray *msg_gsignature = NULL; g_return_if_fail (!DBUS_G_PROXY_DESTROYED (proxy)); interface = dbus_message_get_interface (message); signal = dbus_message_get_member (message); g_assert (interface != NULL); g_assert (signal != NULL); name = create_signal_name (interface, signal); /* If the quark isn't preexisting, there's no way there * are any handlers connected. We don't want to create * extra quarks for every possible signal. */ q = g_quark_try_string (name); if (q != 0) { GArray *gsignature; guint i; gsignature = g_datalist_id_get_data (&priv->signal_signatures, q); if (gsignature == NULL) goto out; msg_gsignature = _dbus_gtypes_from_arg_signature (dbus_message_get_signature (message), TRUE); for (i = 0; i < gsignature->len; i++) { if (msg_gsignature->len == i || g_array_index (gsignature, GType, i) != g_array_index (msg_gsignature, GType, i)) goto mismatch; } if (msg_gsignature->len != i) goto mismatch; g_signal_emit (proxy, signals[RECEIVED], q, message, msg_gsignature); } out: g_free (name); if (msg_gsignature) g_array_free (msg_gsignature, TRUE); return; mismatch: #if 0 /* Don't spew on remote errors */ g_warning ("Unexpected message signature '%s' for signal '%s'\n", dbus_message_get_signature (message), name); #endif goto out; } /** * DBusGProxyCallNotify: * @proxy: the proxy on which the method was called * @call_id: the call in progress * @user_data: data passed to dbus_g_proxy_begin_call() or similar * * Called when a reply to the call represented by @call_id arrives. * Use dbus_g_proxy_end_call() to see whether @call_id succeeded or * failed, and get the arguments returned (if any) on success. * * Deprecated: New code should use GDBus instead. The closest equivalent * is the standard #GAsyncReadyCallback mechanism. */ typedef struct { DBusGProxy *proxy; guint call_id; DBusGProxyCallNotify func; void *data; GDestroyNotify free_data_func; } GPendingNotifyClosure; static void d_pending_call_notify (DBusPendingCall *dcall, void *data) { GPendingNotifyClosure *closure = data; (* closure->func) (closure->proxy, DBUS_G_PROXY_ID_TO_CALL (closure->call_id), closure->data); } static void d_pending_call_free (void *data) { GPendingNotifyClosure *closure = data; if (closure->free_data_func) (* closure->free_data_func) (closure->data); g_free (closure); } #define DBUS_G_VALUE_ARRAY_COLLECT_ALL(VALARRAY, FIRST_ARG_TYPE, ARGS) \ G_STMT_START { \ GType valtype; \ guint i = 0; \ \ VALARRAY = g_value_array_new (6); \ valtype = FIRST_ARG_TYPE; \ \ while (valtype != G_TYPE_INVALID) \ { \ gchar *collect_err; \ GValue *val; \ \ g_value_array_append (VALARRAY, NULL); \ val = g_value_array_get_nth (VALARRAY, i); \ g_value_init (val, valtype); \ collect_err = NULL; \ G_VALUE_COLLECT (val, ARGS, G_VALUE_NOCOPY_CONTENTS, &collect_err); \ \ if (collect_err) \ { \ g_critical ("%s: unable to collect argument %u: %s", \ G_STRFUNC, i, collect_err); \ g_free (collect_err); \ g_value_array_free (VALARRAY); \ VALARRAY = NULL; \ break; \ } \ \ valtype = va_arg (ARGS, GType); \ i++; \ } \ } G_STMT_END DBusGProxyCall * manager_begin_bus_call (DBusGProxyManager *manager, const char *method, DBusGProxyCallNotify notify, gpointer user_data, GDestroyNotify destroy, GType first_arg_type, ...) { guint call_id = 0; DBusGProxyPrivate *priv; va_list args; GValueArray *arg_values; va_start (args, first_arg_type); if (!manager->bus_proxy) { manager->bus_proxy = g_object_new (DBUS_TYPE_G_PROXY, "name", DBUS_SERVICE_DBUS, "path", DBUS_PATH_DBUS, "interface", DBUS_INTERFACE_DBUS, NULL); priv = DBUS_G_PROXY_GET_PRIVATE(manager->bus_proxy); priv->manager = manager; } DBUS_G_VALUE_ARRAY_COLLECT_ALL (arg_values, first_arg_type, args); if (arg_values != NULL) { call_id = dbus_g_proxy_begin_call_internal (manager->bus_proxy, method, notify, user_data, destroy, arg_values, -1); g_value_array_free (arg_values); } va_end (args); return DBUS_G_PROXY_ID_TO_CALL (call_id); } /** * SECTION:dbus-gproxy * @title: DBusGProxy * @short_description: DBus Proxy * @see_also: #DBusGProxy * @stability: Stable * * A #DBusGProxy is a #GObject representing a remote object in a D-Bus * service. * * Deprecated: New code should use #GDBusProxy instead. */ /** * DBusGProxy: * * A #GObject representing a remote object in a D-Bus service. * * Deprecated: New code should use GDBus instead. The closest equivalent * is #GDBusProxy. */ /** * DBusGProxyCall: * * An opaque pointer representing an asynchronous call in progress. * * Deprecated: New code should use GDBus instead. There is no direct * equivalent in GDBus, but the standard #GCancellable mechanism is * analogous. */ /* * dbus_g_proxy_get_type: * Standard GObject get_type() function for DBusGProxy. * * Returns: type ID for DBusGProxy class * * Deprecated: New code should use #GDBusProxy instead. */ GType dbus_g_proxy_get_type (void) { static GType object_type = 0; if (!object_type) { static const GTypeInfo object_info = { sizeof (DBusGProxyClass), (GBaseInitFunc) NULL, (GBaseFinalizeFunc) NULL, (GClassInitFunc) dbus_g_proxy_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof (DBusGProxy), 0, /* n_preallocs */ (GInstanceInitFunc) dbus_g_proxy_init, }; object_type = g_type_register_static (G_TYPE_OBJECT, "DBusGProxy", &object_info, 0); } return object_type; } static DBusGProxy* dbus_g_proxy_new (DBusGConnection *connection, const char *name, const char *path_name, const char *interface_name) { DBusGProxy *proxy; g_assert (connection != NULL); proxy = g_object_new (DBUS_TYPE_G_PROXY, "name", name, "path", path_name, "interface", interface_name, "connection", connection, NULL); return proxy; } /** * dbus_g_proxy_new_for_name: * @connection: the connection to the remote bus * @name: any name on the message bus * @path: name of the object instance to call methods on * @iface: name of the interface to call methods on * * Creates a new proxy for a remote interface exported by a connection * on a message bus. Method calls and signal connections over this * proxy will go to the name owner; the name's owner is expected to * support the given interface name. THE NAME OWNER MAY CHANGE OVER * TIME, for example between two different method calls, unless the * name is a unique name. If you need a fixed owner, you need to * request the current owner and bind a proxy to its unique name * rather than to the generic name; see * dbus_g_proxy_new_for_name_owner(). * * A name-associated proxy only makes sense with a message bus, not * for app-to-app direct dbus connections. * * This proxy will only emit the "destroy" signal if the * #DBusConnection is disconnected, the proxy has no remaining * references, or the name is a unique name and its owner * disappears. If a well-known name changes owner, the proxy will * still be alive. * * Returns: new proxy object * * Deprecated: New code should use GDBus instead. The closest equivalent * is g_dbus_proxy_new_sync(). */ DBusGProxy* dbus_g_proxy_new_for_name (DBusGConnection *connection, const char *name, const char *path, const char *iface) { g_return_val_if_fail (connection != NULL, NULL); g_return_val_if_fail (g_dbus_is_name (name), NULL); g_return_val_if_fail (g_variant_is_object_path (path), NULL); g_return_val_if_fail (g_dbus_is_interface_name (iface), NULL); return dbus_g_proxy_new (connection, name, path, iface); } /** * dbus_g_proxy_new_for_name_owner: * @connection: the connection to the remote bus * @name: any name on the message bus * @path: name of the object inside the service to call methods on * @iface: name of the interface to call methods on * @error: return location for an error * * Similar to dbus_g_proxy_new_for_name(), but makes a round-trip * request to the message bus to get the current name owner, then * binds the proxy to the unique name of the current owner, rather * than to the well-known name. As a result, the name owner will * not change over time, and the proxy will emit the "destroy" signal * when the owner disappears from the message bus. * * An example of the difference between dbus_g_proxy_new_for_name() * and dbus_g_proxy_new_for_name_owner(): if you provide the well-known name * "org.freedesktop.Database" dbus_g_proxy_new_for_name() remains bound * to that name as it changes owner. dbus_g_proxy_new_for_name_owner() * will fail if the name has no owner. If the name has an owner, * dbus_g_proxy_new_for_name_owner() will bind to the unique name * of that owner rather than the generic name. * * Returns: new proxy object, or %NULL on error * * Deprecated: New code should use GDBus instead. The closest equivalent * is g_dbus_proxy_new_sync() with the name owner's unique name * passed as @name. */ DBusGProxy* dbus_g_proxy_new_for_name_owner (DBusGConnection *connection, const char *name, const char *path, const char *iface, GError **error) { DBusGProxy *proxy; char *unique_name; g_return_val_if_fail (connection != NULL, NULL); g_return_val_if_fail (g_dbus_is_name (name), NULL); g_return_val_if_fail (g_variant_is_object_path (path), NULL); g_return_val_if_fail (g_dbus_is_interface_name (iface), NULL); if (!(unique_name = get_name_owner (DBUS_CONNECTION_FROM_G_CONNECTION (connection), name, error))) return NULL; proxy = dbus_g_proxy_new (connection, unique_name, path, iface); g_free (unique_name); return proxy; } /** * dbus_g_proxy_new_from_proxy: * @proxy: the proxy to use as a template * @iface: name of the interface to call methods on * @path: of the object inside the peer to call methods on * * Creates a proxy using an existing proxy as a template, substituting * the specified interface and path. Either or both may be NULL. * * Returns: new proxy object * * Deprecated: New code should use GDBus instead. The closest equivalent * is g_dbus_proxy_new_sync(). */ DBusGProxy* dbus_g_proxy_new_from_proxy (DBusGProxy *proxy, const char *iface, const char *path) { DBusGProxyPrivate *priv; g_return_val_if_fail (DBUS_IS_G_PROXY (proxy), NULL); g_return_val_if_fail (path == NULL || g_variant_is_object_path (path), NULL); g_return_val_if_fail (iface == NULL || g_dbus_is_interface_name (iface), NULL); priv = DBUS_G_PROXY_GET_PRIVATE(proxy); if (iface == NULL) iface = priv->interface; if (path == NULL) path = priv->path; return dbus_g_proxy_new (DBUS_G_CONNECTION_FROM_CONNECTION (priv->manager->connection), priv->name, path, iface); } /** * dbus_g_proxy_new_for_peer: * @connection: the connection to the peer * @path: name of the object inside the peer to call methods on * @iface: name of the interface to call methods on * * Creates a proxy for an object in peer application (one * we're directly connected to). That is, this function is * intended for use when there's no message bus involved, * we're doing a simple 1-to-1 communication between two * applications. * * Returns: new proxy object * * Deprecated: New code should use GDBus instead. The closest equivalent * is g_dbus_proxy_new_sync(). */ DBusGProxy* dbus_g_proxy_new_for_peer (DBusGConnection *connection, const char *path, const char *iface) { DBusGProxy *proxy; g_return_val_if_fail (connection != NULL, NULL); g_return_val_if_fail (g_variant_is_object_path (path), NULL); g_return_val_if_fail (g_dbus_is_interface_name (iface), NULL); proxy = dbus_g_proxy_new (connection, NULL, path, iface); return proxy; } /** * dbus_g_proxy_get_bus_name: * @proxy: the proxy * * Gets the bus name a proxy is bound to (may be %NULL in some cases). * If you created the proxy with dbus_g_proxy_new_for_name(), then * the name you passed to that will be returned. * If you created it with dbus_g_proxy_new_for_name_owner(), then the * unique connection name will be returned. If you created it * with dbus_g_proxy_new_for_peer() then %NULL will be returned. * * It is an error to call this method on a proxy that has emitted * the #DBusGProxy::destroy signal. * * Returns: the bus name the proxy sends messages to * * Deprecated: New code should use GDBus instead. The closest equivalent * is g_dbus_proxy_get_name() or g_dbus_proxy_get_name_owner(), * depending how the proxy was created. */ const char* dbus_g_proxy_get_bus_name (DBusGProxy *proxy) { DBusGProxyPrivate *priv; g_return_val_if_fail (DBUS_IS_G_PROXY (proxy), NULL); g_return_val_if_fail (!DBUS_G_PROXY_DESTROYED (proxy), NULL); priv = DBUS_G_PROXY_GET_PRIVATE(proxy); return priv->name; } /** * dbus_g_proxy_get_interface: * @proxy: the proxy * * Gets the object interface proxy is bound to (may be %NULL in some cases). * * It is an error to call this method on a proxy that has emitted * the #DBusGProxy::destroy signal. * * Returns: an object interface * * Deprecated: New code should use GDBus instead. The closest equivalent * is g_dbus_proxy_get_interface_name(). */ const char* dbus_g_proxy_get_interface (DBusGProxy *proxy) { DBusGProxyPrivate *priv; g_return_val_if_fail (DBUS_IS_G_PROXY (proxy), NULL); g_return_val_if_fail (!DBUS_G_PROXY_DESTROYED (proxy), NULL); priv = DBUS_G_PROXY_GET_PRIVATE(proxy); return priv->interface; } /** * dbus_g_proxy_set_interface: * @proxy: the proxy * @interface_name: an object interface * * Sets the object interface proxy is bound to * * It is an error to call this method on a proxy that has emitted * the #DBusGProxy::destroy signal. * * Deprecated: New code should use GDBus instead. There is no * direct equivalent for this function: construct a new proxy instead. */ void dbus_g_proxy_set_interface (DBusGProxy *proxy, const char *interface_name) { DBusGProxyPrivate *priv = DBUS_G_PROXY_GET_PRIVATE(proxy); g_return_if_fail (DBUS_IS_G_PROXY (proxy)); g_return_if_fail (!DBUS_G_PROXY_DESTROYED (proxy)); g_return_if_fail (g_dbus_is_interface_name (interface_name)); /* FIXME - need to unregister when we switch interface for now * later should support idea of unset interface */ dbus_g_proxy_manager_unregister (priv->manager, proxy); g_free (priv->interface); priv->interface = g_strdup (interface_name); dbus_g_proxy_manager_register (priv->manager, proxy); } /** * dbus_g_proxy_get_path: * @proxy: the proxy * * Gets the path this proxy is bound to * * It is an error to call this method on a proxy that has emitted * the #DBusGProxy::destroy signal. * * Returns: an object path * * Deprecated: New code should use GDBus instead. The closest equivalent * is g_dbus_proxy_get_object_path(). */ const char* dbus_g_proxy_get_path (DBusGProxy *proxy) { DBusGProxyPrivate *priv; g_return_val_if_fail (DBUS_IS_G_PROXY (proxy), NULL); g_return_val_if_fail (!DBUS_G_PROXY_DESTROYED (proxy), NULL); priv = DBUS_G_PROXY_GET_PRIVATE(proxy); return priv->path; } static DBusMessage * dbus_g_proxy_marshal_args_to_message (DBusGProxy *proxy, const char *method, GValueArray *args) { DBusMessage *message; DBusMessageIter msgiter; guint i; DBusGProxyPrivate *priv = DBUS_G_PROXY_GET_PRIVATE(proxy); message = dbus_message_new_method_call (priv->name, priv->path, priv->interface, method); if (message == NULL) return NULL; dbus_message_iter_init_append (message, &msgiter); for (i = 0; i < args->n_values; i++) { GValue *gvalue; gvalue = g_value_array_get_nth (args, i); if (!_dbus_gvalue_marshal (&msgiter, gvalue)) { /* This is a programming error by the caller, most likely */ gchar *contents = g_strdup_value_contents (gvalue); g_critical ("Could not marshal argument %u for %s: type %s, value %s", i, method, G_VALUE_TYPE_NAME (gvalue), contents); g_free (contents); dbus_message_unref (message); return NULL; } } return message; } static guint dbus_g_proxy_begin_call_internal (DBusGProxy *proxy, const char *method, DBusGProxyCallNotify notify, gpointer user_data, GDestroyNotify destroy, GValueArray *args, int timeout) { DBusMessage *message; DBusPendingCall *pending; GPendingNotifyClosure *closure; guint call_id; DBusGProxyPrivate *priv = DBUS_G_PROXY_GET_PRIVATE(proxy); pending = NULL; message = dbus_g_proxy_marshal_args_to_message (proxy, method, args); /* can only happen on a programming error or OOM; we already critical'd */ if (!message) return 0; if (!dbus_connection_send_with_reply (priv->manager->connection, message, &pending, timeout)) oom (); dbus_message_unref (message); /* If we got a NULL pending, that means the connection was disconnected, * and we need to abort this call. * https://bugs.freedesktop.org/show_bug.cgi?id=12675 */ if (pending == NULL) return 0; call_id = ++priv->call_id_counter; if (notify != NULL) { closure = g_new (GPendingNotifyClosure, 1); closure->proxy = proxy; /* No need to ref as the lifecycle is tied to proxy */ closure->call_id = call_id; closure->func = notify; closure->data = user_data; closure->free_data_func = destroy; dbus_pending_call_set_notify (pending, d_pending_call_notify, closure, d_pending_call_free); } g_hash_table_insert (priv->pending_calls, GUINT_TO_POINTER (call_id), pending); return call_id; } static gboolean dbus_g_proxy_end_call_internal (DBusGProxy *proxy, guint call_id, GError **error, GType first_arg_type, va_list args) { DBusMessage *reply; DBusMessageIter msgiter; DBusError derror; va_list args_unwind; guint over; int n_retvals_processed; gboolean ret; GType valtype; DBusPendingCall *pending; DBusGProxyPrivate *priv = DBUS_G_PROXY_GET_PRIVATE(proxy); if (call_id == 0) { /* Being disconnected is the only reason this can happen, except * for programmer error; if it was programmer error, we already * emitted a critical warning. */ g_set_error (error, DBUS_GERROR, DBUS_GERROR_DISCONNECTED, "Disconnected from D-Bus (or argument error during call)"); return FALSE; } reply = NULL; ret = FALSE; n_retvals_processed = 0; over = 0; /* Keep around a copy of output arguments so we can free on error. */ G_VA_COPY(args_unwind, args); pending = g_hash_table_lookup (priv->pending_calls, GUINT_TO_POINTER (call_id)); dbus_pending_call_block (pending); reply = dbus_pending_call_steal_reply (pending); g_assert (reply != NULL); dbus_error_init (&derror); switch (dbus_message_get_type (reply)) { case DBUS_MESSAGE_TYPE_METHOD_RETURN: dbus_message_iter_init (reply, &msgiter); valtype = first_arg_type; while (valtype != G_TYPE_INVALID) { int arg_type; gpointer return_storage; GValue gvalue = { 0, }; DBusGValueMarshalCtx context; context.recursion_depth = 0; context.gconnection = DBUS_G_CONNECTION_FROM_CONNECTION (priv->manager->connection); context.proxy = proxy; arg_type = dbus_message_iter_get_arg_type (&msgiter); if (arg_type == DBUS_TYPE_INVALID) { g_set_error (error, DBUS_GERROR, DBUS_GERROR_INVALID_ARGS, "Too few arguments in reply"); goto out; } return_storage = va_arg (args, gpointer); if (return_storage == NULL) goto next; /* We handle variants specially; the caller is expected * to have already allocated storage for them. */ if (arg_type == DBUS_TYPE_VARIANT && g_type_is_a (valtype, G_TYPE_VALUE)) { if (!_dbus_gvalue_demarshal_variant (&context, &msgiter, (GValue*) return_storage, NULL)) { g_set_error (error, DBUS_GERROR, DBUS_GERROR_INVALID_ARGS, "Couldn't convert argument, expected \"%s\"", g_type_name (valtype)); goto out; } } else { g_value_init (&gvalue, valtype); if (!_dbus_gvalue_demarshal (&context, &msgiter, &gvalue, error)) goto out; /* Anything that can be demarshaled must be storable */ if (!_dbus_gvalue_store (&gvalue, return_storage)) g_assert_not_reached (); /* Ownership of the value passes to the client, don't unset */ } next: n_retvals_processed++; dbus_message_iter_next (&msgiter); valtype = va_arg (args, GType); } while (dbus_message_iter_get_arg_type (&msgiter) != DBUS_TYPE_INVALID) { over++; dbus_message_iter_next (&msgiter); } if (over > 0) { g_set_error (error, DBUS_GERROR, DBUS_GERROR_INVALID_ARGS, "Too many arguments in reply; expected %d, got %d", n_retvals_processed, over); goto out; } break; case DBUS_MESSAGE_TYPE_ERROR: dbus_set_error_from_message (&derror, reply); dbus_set_g_error (error, &derror); dbus_error_free (&derror); goto out; break; default: dbus_set_error (&derror, DBUS_ERROR_FAILED, "Reply was neither a method return nor an exception"); dbus_set_g_error (error, &derror); dbus_error_free (&derror); goto out; break; } ret = TRUE; out: if (ret == FALSE) { int i; valtype = first_arg_type; for (i = 0; i < n_retvals_processed; i++) { GValue value = {0,}; gpointer retval; g_value_init (&value, valtype); retval = va_arg (args_unwind, gpointer); if (retval == NULL) { i--; continue; } _dbus_gvalue_take (&value, retval); g_value_unset (&value); valtype = va_arg (args_unwind, GType); } } va_end (args_unwind); va_end (args); g_hash_table_remove (priv->pending_calls, GUINT_TO_POINTER (call_id)); if (reply) dbus_message_unref (reply); return ret; } /** * dbus_g_proxy_begin_call: * @proxy: a proxy for a remote interface * @method: the name of the method to invoke * @notify: callback to be invoked when method returns * @user_data: user data passed to callback * @destroy: function called to destroy user_data * @first_arg_type: type of the first argument, or %G_TYPE_INVALID if there * are no arguments * @...: first argument, followed by any further type/value pairs, followed * by %G_TYPE_INVALID * * Asynchronously invokes a method on a remote interface. The method * call will not be sent over the wire until the application returns * to the main loop, or blocks in dbus_g_connection_flush() to write out * pending data. The call will be completed after a timeout, or when * a reply is received. When the call returns, the callback specified * will be invoked; you can then collect the results of the call * (which may be an error, or a reply), use dbus_g_proxy_end_call(). * * It is an error to call this method on a proxy that has emitted * the #DBusGProxy::destroy signal. * * TODO this particular function shouldn't die on out of memory, * since you should be able to do a call with large arguments. * * Returns: call identifier. * * Deprecated: New code should use GDBus instead. The closest equivalent * is g_dbus_proxy_call(). */ DBusGProxyCall * dbus_g_proxy_begin_call (DBusGProxy *proxy, const char *method, DBusGProxyCallNotify notify, gpointer user_data, GDestroyNotify destroy, GType first_arg_type, ...) { guint call_id = 0; va_list args; GValueArray *arg_values; DBusGProxyPrivate *priv = DBUS_G_PROXY_GET_PRIVATE(proxy); g_return_val_if_fail (DBUS_IS_G_PROXY (proxy), NULL); g_return_val_if_fail (!DBUS_G_PROXY_DESTROYED (proxy), NULL); g_return_val_if_fail (g_dbus_is_member_name (method), NULL); va_start (args, first_arg_type); DBUS_G_VALUE_ARRAY_COLLECT_ALL (arg_values, first_arg_type, args); if (arg_values != NULL) { call_id = dbus_g_proxy_begin_call_internal (proxy, method, notify, user_data, destroy, arg_values, priv->default_timeout); g_value_array_free (arg_values); } va_end (args); return DBUS_G_PROXY_ID_TO_CALL (call_id); } /** * dbus_g_proxy_begin_call_with_timeout: * @proxy: a proxy for a remote interface * @method: the name of the method to invoke * @notify: callback to be invoked when method returns * @user_data: user data passed to callback * @destroy: function called to destroy user_data * @timeout: the timeout in milliseconds, or -1 to use a default * @first_arg_type: type of the first argument, or %G_TYPE_INVALID if there * are no arguments * @...: first argument, followed by any further type/value pairs, followed * by %G_TYPE_INVALID * * Asynchronously invokes a method on a remote interface. The method * call will not be sent over the wire until the application returns * to the main loop, or blocks in dbus_g_connection_flush() to write out * pending data. The call will be completed after a timeout, or when * a reply is received. When the call returns, the callback specified * will be invoked; you can then collect the results of the call * (which may be an error, or a reply), use dbus_g_proxy_end_call(). * * It is an error to call this method on a proxy that has emitted * the #DBusGProxy::destroy signal. * * TODO this particular function shouldn't die on out of memory, * since you should be able to do a call with large arguments. * * Returns: call identifier. * * Deprecated: New code should use GDBus instead. The closest equivalent * is g_dbus_proxy_call(). */ DBusGProxyCall * dbus_g_proxy_begin_call_with_timeout (DBusGProxy *proxy, const char *method, DBusGProxyCallNotify notify, gpointer user_data, GDestroyNotify destroy, int timeout, GType first_arg_type, ...) { guint call_id = 0; va_list args; GValueArray *arg_values; g_return_val_if_fail (DBUS_IS_G_PROXY (proxy), NULL); g_return_val_if_fail (!DBUS_G_PROXY_DESTROYED (proxy), NULL); g_return_val_if_fail (g_dbus_is_member_name (method), NULL); g_return_val_if_fail (timeout >= 0 || timeout == -1, NULL); va_start (args, first_arg_type); DBUS_G_VALUE_ARRAY_COLLECT_ALL (arg_values, first_arg_type, args); if (arg_values != NULL) { call_id = dbus_g_proxy_begin_call_internal (proxy, method, notify, user_data, destroy, arg_values, timeout); g_value_array_free (arg_values); } va_end (args); return DBUS_G_PROXY_ID_TO_CALL (call_id); } /** * dbus_g_proxy_end_call: * @proxy: a proxy for a remote interface * @call: the pending call ID from dbus_g_proxy_begin_call() * @error: return location for an error * @first_arg_type: type of first "out" argument, or %G_TYPE_INVALID if * there are no "out" arguments * @...: return location for first "out" argument, followed by any further * type/location pairs, followed by %G_TYPE_INVALID * * Collects the results of a method call. The method call was normally * initiated with dbus_g_proxy_end_call(). You may use this function * outside of the callback given to dbus_g_proxy_begin_call; in that * case this function will block if the results haven't yet been * received. * * All D-Bus method calls can fail with a remote error. If this occurs, * the @error will be set and this function will return %FALSE. * * Otherwise, the "out" parameters and return value of the * method are stored in the provided varargs list. * The list should be terminated with G_TYPE_INVALID. * * Returns: %TRUE on success * * Deprecated: New code should use GDBus instead. The closest equivalent * is g_dbus_proxy_call_finish(). */ gboolean dbus_g_proxy_end_call (DBusGProxy *proxy, DBusGProxyCall *call, GError **error, GType first_arg_type, ...) { gboolean ret; va_list args; g_return_val_if_fail (DBUS_IS_G_PROXY (proxy), FALSE); va_start (args, first_arg_type); ret = dbus_g_proxy_end_call_internal (proxy, GPOINTER_TO_UINT (call), error, first_arg_type, args); va_end (args); return ret; } /** * dbus_g_proxy_call: * @proxy: a proxy for a remote interface * @method: method to invoke * @error: return location for an error * @first_arg_type: type of first "in" argument, or %G_TYPE_INVALID if none * @...: value of first "in" argument, any further type/value pairs, * %G_TYPE_INVALID, type/location pairs for "out" arguments, * and %G_TYPE_INVALID again * * Function for synchronously invoking a method and receiving reply * values. This function is equivalent to dbus_g_proxy_begin_call * followed by dbus_g_proxy_end_call. All of the input arguments are * specified first, followed by G_TYPE_INVALID, followed by all of the * output values, followed by a second G_TYPE_INVALID. Note that * this means you must always specify G_TYPE_INVALID twice. * * It is an error to call this method on a proxy that has emitted * the #DBusGProxy::destroy signal. * * Returns: %TRUE if the method succeeds, %FALSE if it fails * * Deprecated: New code should use GDBus instead. The closest equivalent * is g_dbus_proxy_call_sync(). */ gboolean dbus_g_proxy_call (DBusGProxy *proxy, const char *method, GError **error, GType first_arg_type, ...) { gboolean ret; guint call_id = 0; va_list args; GValueArray *in_args; DBusGProxyPrivate *priv; g_return_val_if_fail (DBUS_IS_G_PROXY (proxy), FALSE); g_return_val_if_fail (!DBUS_G_PROXY_DESTROYED (proxy), FALSE); priv = DBUS_G_PROXY_GET_PRIVATE(proxy); va_start (args, first_arg_type); DBUS_G_VALUE_ARRAY_COLLECT_ALL (in_args, first_arg_type, args); if (in_args != NULL) { call_id = dbus_g_proxy_begin_call_internal (proxy, method, NULL, NULL, NULL, in_args, priv->default_timeout); g_value_array_free (in_args); } first_arg_type = va_arg (args, GType); ret = dbus_g_proxy_end_call_internal (proxy, call_id, error, first_arg_type, args); va_end (args); return ret; } /** * dbus_g_proxy_call_with_timeout: * @proxy: a proxy for a remote interface * @method: method to invoke * @timeout: the timeout in milliseconds, or -1 to use a default * @error: return location for an error * @first_arg_type: type of first "in" argument * @...: as for dbus_g_proxy_call() * * Function for synchronously invoking a method and receiving reply * values. This function is equivalent to dbus_g_proxy_begin_call * followed by dbus_g_proxy_end_call. All of the input arguments are * specified first, followed by G_TYPE_INVALID, followed by all of the * output values, followed by a second G_TYPE_INVALID. Note that * this means you must always specify G_TYPE_INVALID twice. * * It is an error to call this method on a proxy that has emitted * the #DBusGProxy::destroy signal. * * Returns: %TRUE if the method succeeds, %FALSE if it fails * * Deprecated: New code should use GDBus instead. The closest equivalent * is g_dbus_proxy_call_sync(). */ gboolean dbus_g_proxy_call_with_timeout (DBusGProxy *proxy, const char *method, int timeout, GError **error, GType first_arg_type, ...) { gboolean ret; guint call_id = 0; va_list args; GValueArray *in_args; g_return_val_if_fail (DBUS_IS_G_PROXY (proxy), FALSE); g_return_val_if_fail (!DBUS_G_PROXY_DESTROYED (proxy), FALSE); g_return_val_if_fail (g_dbus_is_member_name (method), FALSE); g_return_val_if_fail (timeout >= 0 || timeout == -1, FALSE); va_start (args, first_arg_type); DBUS_G_VALUE_ARRAY_COLLECT_ALL (in_args, first_arg_type, args); if (in_args != NULL) { call_id = dbus_g_proxy_begin_call_internal (proxy, method, NULL, NULL, NULL, in_args, timeout); g_value_array_free (in_args); } first_arg_type = va_arg (args, GType); ret = dbus_g_proxy_end_call_internal (proxy, call_id, error, first_arg_type, args); va_end (args); return ret; } /** * dbus_g_proxy_call_no_reply: * @proxy: a proxy for a remote interface * @method: the name of the method to invoke * @first_arg_type: type of the first argument, or %G_TYPE_INVALID to call * the method without arguments * @...: the first argument and any remaining type/argument pairs, followed by * %G_TYPE_INVALID to terminate the list * * Sends a method call message as with dbus_g_proxy_begin_call(), but * does not ask for a reply or allow you to receive one. * * It is an error to call this method on a proxy that has emitted * the #DBusGProxy::destroy signal. * * TODO: this particular function shouldn't die on out of memory, * since you should be able to do a call with large arguments. * * Deprecated: New code should use GDBus instead. The closest equivalent * is g_dbus_proxy_call() with @callback = %NULL. */ void dbus_g_proxy_call_no_reply (DBusGProxy *proxy, const char *method, GType first_arg_type, ...) { DBusMessage *message = NULL; va_list args; GValueArray *in_args; DBusGProxyPrivate *priv; g_return_if_fail (DBUS_IS_G_PROXY (proxy)); g_return_if_fail (g_dbus_is_member_name (method)); g_return_if_fail (!DBUS_G_PROXY_DESTROYED (proxy)); priv = DBUS_G_PROXY_GET_PRIVATE(proxy); va_start (args, first_arg_type); DBUS_G_VALUE_ARRAY_COLLECT_ALL (in_args, first_arg_type, args); if (in_args != NULL) { message = dbus_g_proxy_marshal_args_to_message (proxy, method, in_args); g_value_array_free (in_args); } va_end (args); /* can only happen on a programming error or OOM; we already critical'd */ if (!message) return; dbus_message_set_no_reply (message, TRUE); if (!dbus_connection_send (priv->manager->connection, message, NULL)) oom (); dbus_message_unref (message); } /** * dbus_g_proxy_cancel_call * @proxy: a proxy for a remote interface * @call: the pending call ID from dbus_g_proxy_begin_call() * * Cancels a pending method call. The method call was normally * initiated with dbus_g_proxy_begin_call(). This function * may not be used on pending calls that have already been * ended with dbus_g_proxy_end_call. * * It is an error to call this method on a proxy that has emitted * the #DBusGProxy::destroy signal. * * Deprecated: New code should use GDBus instead. The closest equivalent * is g_cancellable_cancel(). */ void dbus_g_proxy_cancel_call (DBusGProxy *proxy, DBusGProxyCall *call) { guint call_id; DBusPendingCall *pending; DBusGProxyPrivate *priv; g_return_if_fail (DBUS_IS_G_PROXY (proxy)); g_return_if_fail (!DBUS_G_PROXY_DESTROYED (proxy)); priv = DBUS_G_PROXY_GET_PRIVATE(proxy); call_id = DBUS_G_PROXY_CALL_TO_ID (call); if (call_id == 0) { /* nothing to cancel */ return; } pending = g_hash_table_lookup (priv->pending_calls, GUINT_TO_POINTER (call_id)); g_hash_table_remove (priv->pending_calls, GUINT_TO_POINTER (call_id)); g_return_if_fail (pending != NULL); dbus_pending_call_cancel (pending); } /** * dbus_g_proxy_send: * @proxy: a proxy for a remote interface * @message: the message to address and send * @client_serial: return location for message's serial, or %NULL * * Sends a message to the interface we're proxying for. Does not * block or wait for a reply. The message is only actually written out * when you return to the main loop or block in * dbus_g_connection_flush(). * * The message is modified to be addressed to the target interface. * That is, a destination name field or whatever is needed will be * added to the message. The basic point of this function is to add * the necessary header fields, otherwise it's equivalent to * dbus_connection_send(). * * This function adds a reference to the message, so the caller * still owns its original reference. * * It is an error to call this method on a proxy that has emitted * the #DBusGProxy::destroy signal. * * Deprecated: New code should use GDBus instead. The closest equivalent * is g_dbus_connection_send_message(). */ void dbus_g_proxy_send (DBusGProxy *proxy, DBusMessage *message, dbus_uint32_t *client_serial) { DBusGProxyPrivate *priv; g_return_if_fail (DBUS_IS_G_PROXY (proxy)); g_return_if_fail (!DBUS_G_PROXY_DESTROYED (proxy)); priv = DBUS_G_PROXY_GET_PRIVATE(proxy); if (priv->name) { if (!dbus_message_set_destination (message, priv->name)) g_error ("Out of memory"); } if (priv->path) { if (!dbus_message_set_path (message, priv->path)) g_error ("Out of memory"); } if (priv->interface) { if (!dbus_message_set_interface (message, priv->interface)) g_error ("Out of memory"); } if (!dbus_connection_send (priv->manager->connection, message, client_serial)) g_error ("Out of memory\n"); } static void array_free_all (gpointer array) { g_array_free (array, TRUE); } /** * dbus_g_proxy_add_signal: * @proxy: the proxy for a remote interface * @signal_name: the name of the signal * @first_type: the first argument type, or %G_TYPE_INVALID if none * @...: any subsequent argument types, followed by %G_TYPE_INVALID * * Specifies the argument signature of a D-Bus signal. When the signal is * emitted by the remote object, if the GTypes corresponding to its arguments' * types do not match the types given here, the signal will be ignored. * * It is an error to add the same @signal_name to the same @proxy more than * once, even if the argument types given are the same. * * It is also an error to call this method on a proxy that has emitted * the #DBusGProxy::destroy signal. * * Deprecated: New code should use GDBus instead. */ void dbus_g_proxy_add_signal (DBusGProxy *proxy, const char *signal_name, GType first_type, ...) { GQuark q; char *name; GArray *gtypesig; GType gtype; va_list args; DBusGProxyPrivate *priv; g_return_if_fail (DBUS_IS_G_PROXY (proxy)); g_return_if_fail (!DBUS_G_PROXY_DESTROYED (proxy)); g_return_if_fail (g_dbus_is_member_name (signal_name)); priv = DBUS_G_PROXY_GET_PRIVATE(proxy); name = create_signal_name (priv->interface, signal_name); q = g_quark_from_string (name); g_return_if_fail (g_datalist_id_get_data (&priv->signal_signatures, q) == NULL); gtypesig = g_array_new (FALSE, TRUE, sizeof (GType)); va_start (args, first_type); gtype = first_type; while (gtype != G_TYPE_INVALID) { g_array_append_val (gtypesig, gtype); gtype = va_arg (args, GType); } va_end (args); g_datalist_id_set_data_full (&priv->signal_signatures, q, gtypesig, array_free_all); g_free (name); } /** * dbus_g_proxy_connect_signal: * @proxy: a proxy for a remote interface * @signal_name: the DBus signal name to listen for * @handler: the handler to connect * @data: data to pass to handler * @free_data_func: callback function to destroy data * * Connect a signal handler to a proxy for a remote interface. When * the remote interface emits the specified signal, the proxy will * emit a corresponding GLib signal. * * It is an error to call this method on a proxy that has emitted * the #DBusGProxy::destroy signal. * * Deprecated: New code should use GDBus instead. The closest equivalent * is g_dbus_connection_signal_subscribe(). */ void dbus_g_proxy_connect_signal (DBusGProxy *proxy, const char *signal_name, GCallback handler, void *data, GClosureNotify free_data_func) { char *name; GClosure *closure; GQuark q; DBusGProxyPrivate *priv; g_return_if_fail (DBUS_IS_G_PROXY (proxy)); g_return_if_fail (!DBUS_G_PROXY_DESTROYED (proxy)); g_return_if_fail (g_dbus_is_member_name (signal_name)); g_return_if_fail (handler != NULL); priv = DBUS_G_PROXY_GET_PRIVATE(proxy); name = create_signal_name (priv->interface, signal_name); q = g_quark_try_string (name); #ifndef G_DISABLE_CHECKS if (q == 0 || g_datalist_id_get_data (&priv->signal_signatures, q) == NULL) { g_warning ("Must add the signal '%s' with dbus_g_proxy_add_signal() prior to connecting to it\n", name); g_free (name); return; } #endif closure = g_cclosure_new (G_CALLBACK (handler), data, free_data_func); g_signal_connect_closure_by_id (G_OBJECT (proxy), signals[RECEIVED], q, closure, FALSE); g_free (name); } /** * dbus_g_proxy_disconnect_signal: * @proxy: a proxy for a remote interface * @signal_name: the DBus signal name to disconnect * @handler: the handler to disconnect * @data: the data that was registered with handler * * Disconnect all signal handlers from a proxy that match the given * criteria. * * It is an error to call this method on a proxy that has emitted * the #DBusGProxy::destroy signal. * * Deprecated: New code should use GDBus instead. The closest equivalent * is g_dbus_connection_signal_unsubscribe(). */ void dbus_g_proxy_disconnect_signal (DBusGProxy *proxy, const char *signal_name, GCallback handler, void *data) { char *name; GQuark q; DBusGProxyPrivate *priv; g_return_if_fail (DBUS_IS_G_PROXY (proxy)); g_return_if_fail (!DBUS_G_PROXY_DESTROYED (proxy)); g_return_if_fail (g_dbus_is_member_name (signal_name)); g_return_if_fail (handler != NULL); priv = DBUS_G_PROXY_GET_PRIVATE(proxy); name = create_signal_name (priv->interface, signal_name); q = g_quark_try_string (name); if (q != 0) { g_signal_handlers_disconnect_matched (G_OBJECT (proxy), G_SIGNAL_MATCH_DETAIL | G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA, signals[RECEIVED], q, NULL, G_CALLBACK (handler), data); } else { g_warning ("Attempt to disconnect from signal '%s' which is not registered\n", name); } g_free (name); } /** * dbus_g_proxy_set_default_timeout: * @proxy: a proxy for a remote interface * @timeout: the timeout in milliseconds, or -1 to reset to the libdbus default * * Sets the default timeout to use for a proxy. This timeout will be * used in calls where the timeout is not specified, or is specified to be -1. * If this timeout is also set to -1, libdbus will use a reasonable default * value. * * This is useful for long-running operations that takes longer than * the default timeout (which is a on the order of magnitude of tens * of seconds). For some applications, consider using a pattern where * the method returns once the operation is underway * (e.g. immediately) and emits a signal when the operation terminates * (though beware of leaking information with/in the signal return value). * * It is an error to call this method on a proxy that has emitted * the #DBusGProxy::destroy signal. * * Since: 0.75 * * Deprecated: New code should use GDBus instead. */ void dbus_g_proxy_set_default_timeout (DBusGProxy *proxy, int timeout) { DBusGProxyPrivate *priv; g_return_if_fail (DBUS_IS_G_PROXY (proxy)); g_return_if_fail (!DBUS_G_PROXY_DESTROYED (proxy)); g_return_if_fail (timeout >= 0 || timeout == -1); priv = DBUS_G_PROXY_GET_PRIVATE(proxy); priv->default_timeout = timeout; }