/* GIO - GLib Input, Output and Streaming Library * * Copyright (C) 2006-2007 Red Hat, Inc. * * 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 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., 59 Temple Place, Suite 330, * Boston, MA 02111-1307, USA. * * Author: Alexander Larsson */ #include #include #include #include #include #include void _g_dbus_oom (void) { g_error ("DBus failed with out of memory error"); } /* We use _ for escaping, so its not valid */ #define VALID_INITIAL_NAME_CHARACTER(c) \ ( ((c) >= 'A' && (c) <= 'Z') || \ ((c) >= 'a' && (c) <= 'z') ) #define VALID_NAME_CHARACTER(c) \ ( ((c) >= '0' && (c) <= '9') || \ ((c) >= 'A' && (c) <= 'Z') || \ ((c) >= 'a' && (c) <= 'z')) static void append_escaped_name (GString *s, const char *unescaped) { char c; gboolean first; static const gchar hex[16] = "0123456789ABCDEF"; first = TRUE; while ((c = *unescaped++) != 0) { if (first) { if (VALID_INITIAL_NAME_CHARACTER (c)) { g_string_append_c (s, c); continue; } } else { if (VALID_NAME_CHARACTER (c)) { g_string_append_c (s, c); continue; } } first = FALSE; g_string_append_c (s, '_'); g_string_append_c (s, hex[((guchar)c) >> 4]); g_string_append_c (s, hex[((guchar)c) & 0xf]); } } DBusMessage * _dbus_message_new_gerror (DBusMessage *message, GQuark domain, gint code, const gchar *format, ...) { DBusMessage *reply; va_list args; GError error; error.domain = domain; error.code = code; va_start (args, format); error.message = g_strdup_vprintf (format, args); va_end (args); reply = _dbus_message_new_from_gerror (message, &error); g_free (error.message); return reply; } DBusMessage * _dbus_message_new_from_gerror (DBusMessage *message, GError *error) { DBusMessage *reply; GString *str; str = g_string_new ("org.glib.GError."); append_escaped_name (str, g_quark_to_string (error->domain)); g_string_append_printf (str, ".c%d", error->code); reply = dbus_message_new_error (message, str->str, error->message); g_string_free (str, TRUE); return reply; } gboolean _g_error_from_message (DBusMessage *message, GError **error) { DBusError derror; dbus_error_init (&derror); if (dbus_set_error_from_message (&derror, message)) { _g_error_from_dbus (&derror, error); dbus_error_free (&derror); return TRUE; } return FALSE; } static void append_unescaped_dbus_name (GString *s, const char *escaped, const char *end) { guchar c; while (escaped < end) { c = *escaped++; if (c == '_' && escaped < end) { c = g_ascii_xdigit_value (*escaped++) << 4; if (escaped < end) c |= g_ascii_xdigit_value (*escaped++); } g_string_append_c (s, c); } } void _g_dbus_message_iter_append_cstring (DBusMessageIter *iter, const char *str) { DBusMessageIter array; if (str == NULL) str = ""; if (!dbus_message_iter_open_container (iter, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE_AS_STRING, &array)) _g_dbus_oom (); if (!dbus_message_iter_append_fixed_array (&array, DBUS_TYPE_BYTE, &str, strlen (str))) _g_dbus_oom (); if (!dbus_message_iter_close_container (iter, &array)) _g_dbus_oom (); } void _g_dbus_message_iter_append_args_valist (DBusMessageIter *iter, int first_arg_type, va_list var_args) { int type; g_return_if_fail (iter != NULL); type = first_arg_type; while (type != DBUS_TYPE_INVALID) { if (type == G_DBUS_TYPE_CSTRING) { const char **value_p; const char *value; value_p = va_arg (var_args, const char**); value = *value_p; _g_dbus_message_iter_append_cstring (iter, value); } else if (dbus_type_is_basic (type)) { const void *value; value = va_arg (var_args, const void*); if (!dbus_message_iter_append_basic (iter, type, value)) _g_dbus_oom (); } else if (type == DBUS_TYPE_ARRAY) { int element_type; DBusMessageIter array; char buf[2]; element_type = va_arg (var_args, int); buf[0] = element_type; buf[1] = '\0'; if (!dbus_message_iter_open_container (iter, DBUS_TYPE_ARRAY, buf, &array)) _g_dbus_oom (); if (dbus_type_is_fixed (element_type)) { const void **value; int n_elements; value = va_arg (var_args, const void**); n_elements = va_arg (var_args, int); if (!dbus_message_iter_append_fixed_array (&array, element_type, value, n_elements)) _g_dbus_oom (); } else if (element_type == DBUS_TYPE_STRING || element_type == DBUS_TYPE_SIGNATURE || element_type == DBUS_TYPE_OBJECT_PATH) { const char ***value_p; const char **value; int n_elements; int i; value_p = va_arg (var_args, const char***); n_elements = va_arg (var_args, int); value = *value_p; i = 0; while (i < n_elements) { if (!dbus_message_iter_append_basic (&array, element_type, &value[i])) _g_dbus_oom (); ++i; } } else { g_error ("arrays of %d can't be appended with _g_dbus_message_append_args_valist for now\n", element_type); } if (!dbus_message_iter_close_container (iter, &array)) _g_dbus_oom (); } type = va_arg (var_args, int); } } void _g_dbus_message_append_args_valist (DBusMessage *message, int first_arg_type, va_list var_args) { int type; DBusMessageIter iter; g_return_if_fail (message != NULL); type = first_arg_type; dbus_message_iter_init_append (message, &iter); _g_dbus_message_iter_append_args_valist (&iter, first_arg_type, var_args); } dbus_bool_t _g_dbus_message_iter_get_args_valist (DBusMessageIter *iter, DBusError *error, int first_arg_type, va_list var_args) { int spec_type, msg_type, i, dbus_spec_type; dbus_bool_t retval; retval = FALSE; spec_type = first_arg_type; i = 0; while (spec_type != DBUS_TYPE_INVALID) { msg_type = dbus_message_iter_get_arg_type (iter); if (spec_type == G_DBUS_TYPE_CSTRING) dbus_spec_type = DBUS_TYPE_ARRAY; else dbus_spec_type = spec_type; if (msg_type != dbus_spec_type) { dbus_set_error (error, DBUS_ERROR_INVALID_ARGS, "Argument %d is specified to be of type \"%c\", but " "is actually of type \"%c\"\n", i, spec_type, msg_type); goto out; } if (spec_type == G_DBUS_TYPE_CSTRING) { int element_type; char **ptr; const char *str; int n_elements; DBusMessageIter array; element_type = dbus_message_iter_get_element_type (iter); if (DBUS_TYPE_BYTE != element_type) { dbus_set_error (error, DBUS_ERROR_INVALID_ARGS, "Argument %d is specified to be an array of \"char\", but " "is actually an array of \"%d\"\n", i, element_type); goto out; } ptr = va_arg (var_args, char**); g_assert (ptr != NULL); dbus_message_iter_recurse (iter, &array); dbus_message_iter_get_fixed_array (&array, &str, &n_elements); *ptr = g_strndup (str, n_elements); } else if (dbus_type_is_basic (spec_type)) { void *ptr; ptr = va_arg (var_args, void*); g_assert (ptr != NULL); dbus_message_iter_get_basic (iter, ptr); } else if (spec_type == DBUS_TYPE_ARRAY) { int element_type; int spec_element_type; const void **ptr; int *n_elements_p; DBusMessageIter array; spec_element_type = va_arg (var_args, int); element_type = dbus_message_iter_get_element_type (iter); if (spec_element_type != element_type) { dbus_set_error (error, DBUS_ERROR_INVALID_ARGS, "Argument %d is specified to be an array of \"%d\", but " "is actually an array of \"%d\"\n", i, spec_element_type, element_type); goto out; } if (dbus_type_is_fixed (spec_element_type)) { ptr = va_arg (var_args, const void**); n_elements_p = va_arg (var_args, int*); g_assert (ptr != NULL); g_assert (n_elements_p != NULL); dbus_message_iter_recurse (iter, &array); dbus_message_iter_get_fixed_array (&array, ptr, n_elements_p); } else if (spec_element_type == DBUS_TYPE_STRING || spec_element_type == DBUS_TYPE_SIGNATURE || spec_element_type == DBUS_TYPE_OBJECT_PATH) { char ***str_array_p; int n_elements; char **str_array; str_array_p = va_arg (var_args, char***); n_elements_p = va_arg (var_args, int*); g_assert (str_array_p != NULL); g_assert (n_elements_p != NULL); /* Count elements in the array */ dbus_message_iter_recurse (iter, &array); n_elements = 0; while (dbus_message_iter_get_arg_type (&array) != DBUS_TYPE_INVALID) { ++n_elements; dbus_message_iter_next (&array); } str_array = g_new0 (char*, n_elements + 1); if (str_array == NULL) { _g_dbus_oom (); goto out; } /* Now go through and dup each string */ dbus_message_iter_recurse (iter, &array); i = 0; while (i < n_elements) { const char *s; dbus_message_iter_get_basic (&array, &s); str_array[i] = g_strdup (s); if (str_array[i] == NULL) { g_strfreev (str_array); _g_dbus_oom (); goto out; } ++i; if (!dbus_message_iter_next (&array)) g_assert (i == n_elements); } g_assert (dbus_message_iter_get_arg_type (&array) == DBUS_TYPE_INVALID); g_assert (i == n_elements); g_assert (str_array[i] == NULL); *str_array_p = str_array; *n_elements_p = n_elements; } } spec_type = va_arg (var_args, int); if (!dbus_message_iter_next (iter) && spec_type != DBUS_TYPE_INVALID) { dbus_set_error (error, DBUS_ERROR_INVALID_ARGS, "Message has only %d arguments, but more were expected", i); goto out; } i++; } retval = TRUE; out: return retval; } dbus_bool_t _g_dbus_message_iter_get_args (DBusMessageIter *iter, DBusError *error, int first_arg_type, ...) { va_list var_args; dbus_bool_t res; va_start (var_args, first_arg_type); res = _g_dbus_message_iter_get_args_valist (iter, error, first_arg_type, var_args); va_end (var_args); return res; } /* Same as the dbus one, except doesn't give OOM and handles G_DBUS_TYPE_CSTRING */ void _g_dbus_message_append_args (DBusMessage *message, int first_arg_type, ...) { va_list var_args; g_return_if_fail (message != NULL); va_start (var_args, first_arg_type); _g_dbus_message_append_args_valist (message, first_arg_type, var_args); va_end (var_args); } void _g_dbus_message_iter_append_args (DBusMessageIter *iter, int first_arg_type, ...) { va_list var_args; g_return_if_fail (iter != NULL); va_start (var_args, first_arg_type); _g_dbus_message_iter_append_args_valist (iter, first_arg_type, var_args); va_end (var_args); } void _g_error_from_dbus (DBusError *derror, GError **error) { const char *name, *end; GString *str; GQuark domain; int code; if (g_str_has_prefix (derror->name, "org.glib.GError.")) { domain = 0; code = 0; name = derror->name + strlen ("org.glib.GError."); end = strchr (name, '.'); if (end) { str = g_string_new (NULL); append_unescaped_dbus_name (str, name, end); domain = g_quark_from_string (str->str); g_string_free (str, TRUE); end++; /* skip . */ if (*end++ == 'c') code = atoi (end); } g_set_error_literal (error, domain, code, derror->message); } /* TODO: Special case other types, like DBUS_ERROR_NO_MEMORY etc? */ else g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "DBus error %s: %s", derror->name, derror->message); } GList * _g_dbus_bus_list_names_with_prefix (DBusConnection *connection, const char *prefix, DBusError *error) { DBusMessage *message, *reply; DBusMessageIter iter, array; GList *names; g_return_val_if_fail (connection != NULL, NULL); message = dbus_message_new_method_call (DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS, "ListNames"); if (message == NULL) return NULL; reply = dbus_connection_send_with_reply_and_block (connection, message, -1, error); dbus_message_unref (message); if (reply == NULL) return NULL; names = NULL; if (!dbus_message_iter_init (reply, &iter) || (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_ARRAY) || (dbus_message_iter_get_element_type (&iter) != DBUS_TYPE_STRING)) goto out; for (dbus_message_iter_recurse (&iter, &array); dbus_message_iter_get_arg_type (&array) == DBUS_TYPE_STRING; dbus_message_iter_next (&array)) { char *name; dbus_message_iter_get_basic (&array, &name); if (g_str_has_prefix (name, prefix)) names = g_list_prepend (names, g_strdup (name)); } names = g_list_reverse (names); out: dbus_message_unref (reply); return names; } /************************************************************************* * Helper fd source * ************************************************************************/ typedef struct { GSource source; GPollFD pollfd; GCancellable *cancellable; gulong cancelled_tag; } FDSource; static gboolean fd_source_prepare (GSource *source, gint *timeout) { FDSource *fd_source = (FDSource *)source; *timeout = -1; return g_cancellable_is_cancelled (fd_source->cancellable); } static gboolean fd_source_check (GSource *source) { FDSource *fd_source = (FDSource *)source; return g_cancellable_is_cancelled (fd_source->cancellable) || fd_source->pollfd.revents != 0; } static gboolean fd_source_dispatch (GSource *source, GSourceFunc callback, gpointer user_data) { GFDSourceFunc func = (GFDSourceFunc)callback; FDSource *fd_source = (FDSource *)source; g_assert (func != NULL); return (*func) (user_data, fd_source->pollfd.revents, fd_source->pollfd.fd); } static void fd_source_finalize (GSource *source) { FDSource *fd_source = (FDSource *)source; if (fd_source->cancelled_tag) g_cancellable_disconnect (fd_source->cancellable, fd_source->cancelled_tag); if (fd_source->cancellable) g_object_unref (fd_source->cancellable); } static GSourceFuncs fd_source_funcs = { fd_source_prepare, fd_source_check, fd_source_dispatch, fd_source_finalize }; /* Might be called on another thread */ static void fd_source_cancelled_cb (GCancellable *cancellable, gpointer data) { /* Wake up the mainloop in case we're waiting on async calls with FDSource */ g_main_context_wakeup (NULL); } /* Two __ to avoid conflict with gio version */ GSource * __g_fd_source_new (int fd, gushort events, GCancellable *cancellable) { GSource *source; FDSource *fd_source; source = g_source_new (&fd_source_funcs, sizeof (FDSource)); fd_source = (FDSource *)source; if (cancellable) fd_source->cancellable = g_object_ref (cancellable); fd_source->pollfd.fd = fd; fd_source->pollfd.events = events; g_source_add_poll (source, &fd_source->pollfd); if (cancellable) fd_source->cancelled_tag = g_cancellable_connect (cancellable, (GCallback)fd_source_cancelled_cb, NULL, NULL); return source; } /************************************************************************* * * * dbus mainloop integration for async ops * * * *************************************************************************/ static gint32 main_integration_data_slot = -1; static GOnce once_init_main_integration = G_ONCE_INIT; /** * A GSource subclass for dispatching DBusConnection messages. * We need this on top of the IO handlers, because sometimes * there are messages to dispatch queued up but no IO pending. * * The source is owned by the connection (and the main context * while that is alive) */ typedef struct { GSource source; DBusConnection *connection; GSList *ios; GSList *timeouts; } DBusSource; typedef struct { DBusSource *dbus_source; GSource *source; DBusWatch *watch; } IOHandler; typedef struct { DBusSource *dbus_source; GSource *source; DBusTimeout *timeout; } TimeoutHandler; static gpointer main_integration_init (gpointer arg) { if (!dbus_connection_allocate_data_slot (&main_integration_data_slot)) g_error ("Unable to allocate data slot"); return NULL; } static gboolean dbus_source_prepare (GSource *source, gint *timeout) { DBusConnection *connection = ((DBusSource *)source)->connection; *timeout = -1; return (dbus_connection_get_dispatch_status (connection) == DBUS_DISPATCH_DATA_REMAINS); } static gboolean dbus_source_check (GSource *source) { return FALSE; } static gboolean dbus_source_dispatch (GSource *source, GSourceFunc callback, gpointer user_data) { DBusConnection *connection = ((DBusSource *)source)->connection; dbus_connection_ref (connection); /* Only dispatch once - we don't want to starve other GSource */ dbus_connection_dispatch (connection); dbus_connection_unref (connection); return TRUE; } static gboolean io_handler_dispatch (gpointer data, GIOCondition condition, int fd) { IOHandler *handler = data; guint dbus_condition = 0; DBusConnection *connection; connection = handler->dbus_source->connection; if (connection) dbus_connection_ref (connection); if (condition & G_IO_IN) dbus_condition |= DBUS_WATCH_READABLE; if (condition & G_IO_OUT) dbus_condition |= DBUS_WATCH_WRITABLE; if (condition & G_IO_ERR) dbus_condition |= DBUS_WATCH_ERROR; if (condition & G_IO_HUP) dbus_condition |= DBUS_WATCH_HANGUP; /* Note that we don't touch the handler after this, because * dbus may have disabled the watch and thus killed the * handler. */ dbus_watch_handle (handler->watch, dbus_condition); handler = NULL; if (connection) dbus_connection_unref (connection); return TRUE; } static void io_handler_free (IOHandler *handler) { DBusSource *dbus_source; dbus_source = handler->dbus_source; dbus_source->ios = g_slist_remove (dbus_source->ios, handler); g_source_destroy (handler->source); g_source_unref (handler->source); g_free (handler); } static void dbus_source_add_watch (DBusSource *dbus_source, DBusWatch *watch) { guint flags; GIOCondition condition; IOHandler *handler; int fd; if (!dbus_watch_get_enabled (watch)) return; g_assert (dbus_watch_get_data (watch) == NULL); flags = dbus_watch_get_flags (watch); condition = G_IO_ERR | G_IO_HUP; if (flags & DBUS_WATCH_READABLE) condition |= G_IO_IN; if (flags & DBUS_WATCH_WRITABLE) condition |= G_IO_OUT; handler = g_new0 (IOHandler, 1); handler->dbus_source = dbus_source; handler->watch = watch; #if (DBUS_MAJOR_VERSION == 1 && DBUS_MINOR_VERSION == 1 && DBUS_MICRO_VERSION >= 1) || (DBUS_MAJOR_VERSION == 1 && DBUS_MINOR_VERSION > 1) || (DBUS_MAJOR_VERSION > 1) fd = dbus_watch_get_unix_fd (watch); #else fd = dbus_watch_get_fd (watch); #endif handler->source = __g_fd_source_new (fd, condition, NULL); g_source_set_callback (handler->source, (GSourceFunc) io_handler_dispatch, handler, NULL); g_source_attach (handler->source, NULL); dbus_source->ios = g_slist_prepend (dbus_source->ios, handler); dbus_watch_set_data (watch, handler, (DBusFreeFunction)io_handler_free); } static void dbus_source_remove_watch (DBusSource *dbus_source, DBusWatch *watch) { dbus_watch_set_data (watch, NULL, NULL); } static void timeout_handler_free (TimeoutHandler *handler) { DBusSource *dbus_source; dbus_source = handler->dbus_source; dbus_source->timeouts = g_slist_remove (dbus_source->timeouts, handler); g_source_destroy (handler->source); g_source_unref (handler->source); g_free (handler); } static gboolean timeout_handler_dispatch (gpointer data) { TimeoutHandler *handler = data; dbus_timeout_handle (handler->timeout); return TRUE; } static void dbus_source_add_timeout (DBusSource *dbus_source, DBusTimeout *timeout) { TimeoutHandler *handler; if (!dbus_timeout_get_enabled (timeout)) return; g_assert (dbus_timeout_get_data (timeout) == NULL); handler = g_new0 (TimeoutHandler, 1); handler->dbus_source = dbus_source; handler->timeout = timeout; handler->source = g_timeout_source_new (dbus_timeout_get_interval (timeout)); g_source_set_callback (handler->source, timeout_handler_dispatch, handler, NULL); g_source_attach (handler->source, NULL); /* handler->source is owned by the context here */ dbus_source->timeouts = g_slist_prepend (dbus_source->timeouts, handler); dbus_timeout_set_data (timeout, handler, (DBusFreeFunction)timeout_handler_free); } static void dbus_source_remove_timeout (DBusSource *source, DBusTimeout *timeout) { dbus_timeout_set_data (timeout, NULL, NULL); } static dbus_bool_t add_watch (DBusWatch *watch, gpointer data) { DBusSource *dbus_source = data; dbus_source_add_watch (dbus_source, watch); return TRUE; } static void remove_watch (DBusWatch *watch, gpointer data) { DBusSource *dbus_source = data; dbus_source_remove_watch (dbus_source, watch); } static void watch_toggled (DBusWatch *watch, void *data) { /* Because we just exit on OOM, enable/disable is * no different from add/remove */ if (dbus_watch_get_enabled (watch)) add_watch (watch, data); else remove_watch (watch, data); } static dbus_bool_t add_timeout (DBusTimeout *timeout, void *data) { DBusSource *source = data; if (!dbus_timeout_get_enabled (timeout)) return TRUE; dbus_source_add_timeout (source, timeout); return TRUE; } static void remove_timeout (DBusTimeout *timeout, void *data) { DBusSource *source = data; dbus_source_remove_timeout (source, timeout); } static void timeout_toggled (DBusTimeout *timeout, void *data) { /* Because we just exit on OOM, enable/disable is * no different from add/remove */ if (dbus_timeout_get_enabled (timeout)) add_timeout (timeout, data); else remove_timeout (timeout, data); } static void wakeup_main (void *data) { g_main_context_wakeup (NULL); } static const GSourceFuncs dbus_source_funcs = { dbus_source_prepare, dbus_source_check, dbus_source_dispatch }; /* Called when the connection dies or when we're unintegrating from mainloop */ static void dbus_source_free (DBusSource *dbus_source) { while (dbus_source->ios) { IOHandler *handler = dbus_source->ios->data; dbus_watch_set_data (handler->watch, NULL, NULL); } while (dbus_source->timeouts) { TimeoutHandler *handler = dbus_source->timeouts->data; dbus_timeout_set_data (handler->timeout, NULL, NULL); } /* Remove from mainloop */ g_source_destroy ((GSource *)dbus_source); g_source_unref ((GSource *)dbus_source); } void _g_dbus_connection_integrate_with_main (DBusConnection *connection) { DBusSource *dbus_source; g_once (&once_init_main_integration, main_integration_init, NULL); g_assert (connection != NULL); _g_dbus_connection_remove_from_main (connection); dbus_source = (DBusSource *) g_source_new ((GSourceFuncs*)&dbus_source_funcs, sizeof (DBusSource)); dbus_source->connection = connection; if (!dbus_connection_set_watch_functions (connection, add_watch, remove_watch, watch_toggled, dbus_source, NULL)) _g_dbus_oom (); if (!dbus_connection_set_timeout_functions (connection, add_timeout, remove_timeout, timeout_toggled, dbus_source, NULL)) _g_dbus_oom (); dbus_connection_set_wakeup_main_function (connection, wakeup_main, dbus_source, NULL); /* Owned by both connection and mainloop (until destroy) */ g_source_attach ((GSource *)dbus_source, NULL); if (!dbus_connection_set_data (connection, main_integration_data_slot, dbus_source, (DBusFreeFunction)dbus_source_free)) _g_dbus_oom (); } void _g_dbus_connection_remove_from_main (DBusConnection *connection) { g_once (&once_init_main_integration, main_integration_init, NULL); if (!dbus_connection_set_data (connection, main_integration_data_slot, NULL, NULL)) _g_dbus_oom (); } void _g_dbus_message_iter_copy (DBusMessageIter *dest, DBusMessageIter *source) { int type, element_type; while (dbus_message_iter_get_arg_type (source) != DBUS_TYPE_INVALID) { type = dbus_message_iter_get_arg_type (source); if (dbus_type_is_basic (type)) { dbus_uint64_t value; dbus_message_iter_get_basic (source, &value); dbus_message_iter_append_basic (dest, type, &value); } else if (type == DBUS_TYPE_ARRAY) { DBusMessageIter source_array, dest_array; void *value; int n_elements; char buf[2]; element_type = dbus_message_iter_get_element_type (source); if (dbus_type_is_fixed (element_type)) { buf[0] = element_type; buf[1] = '\0'; dbus_message_iter_recurse (source, &source_array); dbus_message_iter_get_fixed_array (&source_array, &value, &n_elements); if (!dbus_message_iter_open_container (dest, DBUS_TYPE_ARRAY, buf, &dest_array)) _g_dbus_oom (); if (!dbus_message_iter_append_fixed_array (&dest_array, element_type, &value, n_elements)) _g_dbus_oom (); if (!dbus_message_iter_close_container (dest, &dest_array)) _g_dbus_oom (); } else g_error ("Unsupported array type %c in _g_dbus_message_iter_copy", element_type); } else g_error ("Unsupported type %c in _g_dbus_message_iter_copy", type); dbus_message_iter_next (source); } } typedef struct { GAsyncDBusCallback callback; gpointer user_data; GError *io_error; /* protected by async_call lock: */ gboolean ran; /* the pending_call reply handler ran */ gboolean idle; /* we queued an idle */ /* only used for idle */ DBusPendingCall *pending; } AsyncDBusCallData; /* Lock to protect the data for working around racecondition between send_with_reply and pending_set_notify */ G_LOCK_DEFINE_STATIC(async_call); static void handle_async_reply (DBusPendingCall *pending, AsyncDBusCallData *data) { DBusMessage *reply; GError *error; reply = dbus_pending_call_steal_reply (pending); error = NULL; if (_g_error_from_message (reply, &error)) { if (data->callback) data->callback (NULL, error, data->user_data); g_error_free (error); } else { if (data->callback) data->callback (reply, NULL, data->user_data); } dbus_message_unref (reply); } static void async_call_reply (DBusPendingCall *pending, void *_data) { AsyncDBusCallData *data = _data; G_LOCK (async_call); if (data->idle) return; data->ran = TRUE; G_UNLOCK (async_call); handle_async_reply (pending, data); } static gboolean idle_async_callback (void *_data) { AsyncDBusCallData *data = _data; handle_async_reply (data->pending, data); dbus_pending_call_unref (data->pending); return FALSE; } static gboolean async_call_error_at_idle (gpointer _data) { AsyncDBusCallData *data = _data; if (data->callback) data->callback (NULL, data->io_error, data->user_data); g_error_free (data->io_error); g_free (data); return FALSE; } void _g_dbus_connection_call_async (DBusConnection *connection, DBusMessage *message, int timeout_msecs, GAsyncDBusCallback callback, gpointer user_data) { AsyncDBusCallData *data; DBusPendingCall *pending_call; DBusError derror; data = g_new0 (AsyncDBusCallData, 1); data->callback = callback; data->user_data = user_data; if (connection == NULL) { dbus_error_init (&derror); connection = dbus_bus_get (DBUS_BUS_SESSION, &derror); if (connection == NULL) { g_set_error_literal (&data->io_error, G_IO_ERROR, G_IO_ERROR_FAILED, "Can't open dbus connection"); g_idle_add (async_call_error_at_idle, data); dbus_error_free (&derror); return; } } if (!dbus_connection_send_with_reply (connection, message, &pending_call, timeout_msecs)) _g_dbus_oom (); if (pending_call == NULL) { g_set_error (&data->io_error, G_IO_ERROR, G_IO_ERROR_FAILED, "Error while getting peer-to-peer dbus connection: %s", "Connection is closed"); g_idle_add (async_call_error_at_idle, data); return; } if (!dbus_pending_call_set_notify (pending_call, async_call_reply, data, g_free)) _g_dbus_oom (); /* All this is required to work around a race condition between * send_with_reply and pending_call_set_notify :/ */ G_LOCK (async_call); if (dbus_pending_call_get_completed (pending_call) && !data->ran) { data->idle = TRUE; data->pending = dbus_pending_call_ref (pending_call); g_idle_add (idle_async_callback, data); } G_UNLOCK (async_call); dbus_pending_call_unref (pending_call); }