X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=gio%2Fgkdbus.c;h=2a68d40fbffeda0252e2f77b18c76b7cf3ebeaf9;hb=2a53b4d0e2c98a14aedf31e38f0ad1fb2e8fe26f;hp=e612b8f34c9fd2011d27b9360df762ba78fbc987;hpb=8b95838eee737b358a45ebc0eac292eb63a9f68b;p=platform%2Fupstream%2Fglib.git diff --git a/gio/gkdbus.c b/gio/gkdbus.c index e612b8f..2a68d40 100644 --- a/gio/gkdbus.c +++ b/gio/gkdbus.c @@ -26,7 +26,6 @@ #include "glib-unix.h" #include "glibintl.h" #include "kdbus.h" -#include "gkdbusconnection.h" #include #include @@ -34,6 +33,7 @@ #include #include #include +#include #ifdef HAVE_SYS_FILIO_H # include @@ -43,6 +43,15 @@ #include #endif +#include +#include +#include +#include + +#include "glibintl.h" +#include "gunixfdmessage.h" + +#define KDBUS_TIMEOUT_NS 2000000000LU #define KDBUS_POOL_SIZE (16 * 1024LU * 1024LU) #define KDBUS_ALIGN8(l) (((l) + 7) & ~7) #define KDBUS_ALIGN8_PTR(p) ((void*) (uintptr_t)(p)) @@ -56,20 +65,29 @@ for (item = (head)->first; \ (guint8 *)(item) < (guint8 *)(head) + (head)->size; \ item = KDBUS_ITEM_NEXT(item)) +#define KDBUS_FOREACH(iter, first, _size) \ + for (iter = (first); \ + ((guint8 *)(iter) < (guint8 *)(first) + (_size)) && \ + ((guint8 *)(iter) >= (guint8 *)(first)); \ + iter = (void*)(((guint8 *)iter) + KDBUS_ALIGN8((iter)->size))) #define g_alloca0(x) memset(g_alloca(x), '\0', (x)) -static void g_kdbus_initable_iface_init (GInitableIface *iface); -static gboolean g_kdbus_initable_init (GInitable *initable, - GCancellable *cancellable, - GError **error); +struct dbus_fixed_header { + guint8 endian; + guint8 type; + guint8 flags; + guint8 version; + guint32 reserved; + guint64 serial; +}; + +#define DBUS_FIXED_HEADER_TYPE ((const GVariantType *) "(yyyyut)") +#define DBUS_EXTENDED_HEADER_TYPE ((const GVariantType *) "a{tv}") +#define DBUS_MESSAGE_TYPE ((const GVariantType *) "((yyyyut)a{tv}v)") -#define g_kdbus_get_type _g_kdbus_get_type -G_DEFINE_TYPE_WITH_CODE (GKdbus, g_kdbus, G_TYPE_OBJECT, - G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, - g_kdbus_initable_iface_init)); +#define KDBUS_MSG_MAX_SIZE 8192 -/* GBusCredentialsFlags */ typedef enum { G_BUS_CREDS_PID = 1, @@ -78,13 +96,18 @@ typedef enum G_BUS_CREDS_SELINUX_CONTEXT = 4 } GBusCredentialsFlags; -/* GKdbusPrivate struct */ -struct _GKdbusPrivate +typedef GObjectClass GKDBusWorkerClass; + +struct _GKDBusWorker { + GObject parent_instance; + gint fd; + GMainContext *context; + GSource *source; + gchar *kdbus_buffer; - struct kdbus_msg *kmsg; gchar *unique_name; guint64 unique_id; @@ -93,348 +116,383 @@ struct _GKdbusPrivate guint64 attach_flags_send; guint64 attach_flags_recv; + gsize bloom_size; + guint bloom_n_hash; + guint closed : 1; guint inited : 1; guint timeout; guint timed_out : 1; guchar bus_id[16]; -}; -/* GKdbusSource struct */ -typedef struct { - GSource source; - GPollFD pollfd; - GKdbus *kdbus; - GIOCondition condition; - GCancellable *cancellable; - GPollFD cancel_pollfd; - gint64 timeout_time; -} GKdbusSource; + GDBusCapabilityFlags capabilities; + GDBusWorkerMessageReceivedCallback message_received_callback; + GDBusWorkerMessageAboutToBeSentCallback message_about_to_be_sent_callback; + GDBusWorkerDisconnectedCallback disconnected_callback; + gpointer user_data; +}; +static gssize _g_kdbus_receive (GKDBusWorker *kdbus, + GCancellable *cancellable, + GError **error); -typedef gboolean (*GKdbusSourceFunc) (GKdbus *kdbus, - GIOCondition condition, - gpointer user_data); +G_DEFINE_TYPE (GKDBusWorker, g_kdbus_worker, G_TYPE_OBJECT) -/** - * g_kdbus_finalize: - * - */ -static void -g_kdbus_finalize (GObject *object) +/* Hash keys for bloom filters*/ +const guint8 hash_keys[8][16] = { - GKdbus *kdbus = G_KDBUS (object); - - if (kdbus->priv->kdbus_buffer != NULL) - munmap (kdbus->priv->kdbus_buffer, KDBUS_POOL_SIZE); + {0xb9,0x66,0x0b,0xf0,0x46,0x70,0x47,0xc1,0x88,0x75,0xc4,0x9c,0x54,0xb9,0xbd,0x15}, + {0xaa,0xa1,0x54,0xa2,0xe0,0x71,0x4b,0x39,0xbf,0xe1,0xdd,0x2e,0x9f,0xc5,0x4a,0x3b}, + {0x63,0xfd,0xae,0xbe,0xcd,0x82,0x48,0x12,0xa1,0x6e,0x41,0x26,0xcb,0xfa,0xa0,0xc8}, + {0x23,0xbe,0x45,0x29,0x32,0xd2,0x46,0x2d,0x82,0x03,0x52,0x28,0xfe,0x37,0x17,0xf5}, + {0x56,0x3b,0xbf,0xee,0x5a,0x4f,0x43,0x39,0xaf,0xaa,0x94,0x08,0xdf,0xf0,0xfc,0x10}, + {0x31,0x80,0xc8,0x73,0xc7,0xea,0x46,0xd3,0xaa,0x25,0x75,0x0f,0x9e,0x4c,0x09,0x29}, + {0x7d,0xf7,0x18,0x4b,0x7b,0xa4,0x44,0xd5,0x85,0x3c,0x06,0xe0,0x65,0x53,0x96,0x6d}, + {0xf2,0x77,0xe9,0x6f,0x93,0xb5,0x4e,0x71,0x9a,0x0c,0x34,0x88,0x39,0x25,0xbf,0x35} +}; - kdbus->priv->kdbus_buffer = NULL; +enum { + MATCH_ELEMENT_TYPE, + MATCH_ELEMENT_SENDER, + MATCH_ELEMENT_INTERFACE, + MATCH_ELEMENT_MEMBER, + MATCH_ELEMENT_PATH, + MATCH_ELEMENT_PATH_NAMESPACE, + MATCH_ELEMENT_DESTINATION, + MATCH_ELEMENT_ARG0NAMESPACE, + MATCH_ELEMENT_EAVESDROP, + MATCH_ELEMENT_ARGN, + MATCH_ELEMENT_ARGNPATH, +}; - if (kdbus->priv->fd != -1 && !kdbus->priv->closed) - _g_kdbus_close (kdbus, NULL); +/* MatchElement struct */ +typedef struct { + guint16 type; + guint16 arg; + char *value; +} MatchElement; - if (G_OBJECT_CLASS (g_kdbus_parent_class)->finalize) - (*G_OBJECT_CLASS (g_kdbus_parent_class)->finalize) (object); -} +/* Match struct */ +typedef struct { + int n_elements; + MatchElement *elements; +} Match; /** - * g_kdbus_class_init: + * is_key() * */ -static void -g_kdbus_class_init (GKdbusClass *klass) +static gboolean +is_key (const char *key_start, const char *key_end, char *value) { - GObjectClass *gobject_class G_GNUC_UNUSED = G_OBJECT_CLASS (klass); - - g_type_class_add_private (klass, sizeof (GKdbusPrivate)); - gobject_class->finalize = g_kdbus_finalize; -} + gsize len = strlen (value); + if (len != key_end - key_start) + return FALSE; -/** - * g_kdbus_initable_iface_init: - * - */ -static void -g_kdbus_initable_iface_init (GInitableIface *iface) -{ - iface->init = g_kdbus_initable_init; + return strncmp (key_start, value, len) == 0; } /** - * g_kdbus_init: + * parse_key() * */ -static void -g_kdbus_init (GKdbus *kdbus) +static gboolean +parse_key (MatchElement *element, const char *key_start, const char *key_end) { - kdbus->priv = G_TYPE_INSTANCE_GET_PRIVATE (kdbus, G_TYPE_KDBUS, GKdbusPrivate); + gboolean res = TRUE; - kdbus->priv->fd = -1; + if (is_key (key_start, key_end, "type")) + { + element->type = MATCH_ELEMENT_TYPE; + } + else if (is_key (key_start, key_end, "sender")) + { + element->type = MATCH_ELEMENT_SENDER; + } + else if (is_key (key_start, key_end, "interface")) + { + element->type = MATCH_ELEMENT_INTERFACE; + } + else if (is_key (key_start, key_end, "member")) + { + element->type = MATCH_ELEMENT_MEMBER; + } + else if (is_key (key_start, key_end, "path")) + { + element->type = MATCH_ELEMENT_PATH; + } + else if (is_key (key_start, key_end, "path_namespace")) + { + element->type = MATCH_ELEMENT_PATH_NAMESPACE; + } + else if (is_key (key_start, key_end, "destination")) + { + element->type = MATCH_ELEMENT_DESTINATION; + } + else if (is_key (key_start, key_end, "arg0namespace")) + { + element->type = MATCH_ELEMENT_ARG0NAMESPACE; + } + else if (is_key (key_start, key_end, "eavesdrop")) + { + element->type = MATCH_ELEMENT_EAVESDROP; + } + else if (key_end - key_start > 3 && is_key (key_start, key_start + 3, "arg")) + { + const char *digits = key_start + 3; + const char *end_digits = digits; - kdbus->priv->unique_id = -1; - kdbus->priv->unique_name = NULL; + while (end_digits < key_end && g_ascii_isdigit (*end_digits)) + end_digits++; - kdbus->priv->kdbus_buffer = NULL; + if (end_digits == key_end) /* argN */ + { + element->type = MATCH_ELEMENT_ARGN; + element->arg = atoi (digits); + } + else if (is_key (end_digits, key_end, "path")) /* argNpath */ + { + element->type = MATCH_ELEMENT_ARGNPATH; + element->arg = atoi (digits); + } + else + res = FALSE; + } + else + res = FALSE; - kdbus->priv->flags = 0; /* KDBUS_HELLO_ACCEPT_FD */ - kdbus->priv->attach_flags_send = _KDBUS_ATTACH_ALL; - kdbus->priv->attach_flags_recv = _KDBUS_ATTACH_ALL; + return res; } /** - * g_kdbus_initable_init: + * parse_value() * */ -static gboolean -g_kdbus_initable_init (GInitable *initable, - GCancellable *cancellable, - GError **error) +static const char * +parse_value (MatchElement *element, const char *s) { - GKdbus *kdbus; + char quote_char; + GString *value; - g_return_val_if_fail (G_IS_KDBUS (initable), FALSE); + value = g_string_new (""); - kdbus = G_KDBUS (initable); + quote_char = 0; - if (cancellable != NULL) + for (;*s; s++) { - g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, - _("Cancellable initialization not supported")); - return FALSE; + if (quote_char == 0) + { + switch (*s) + { + case '\'': + quote_char = '\''; + break; + + case ',': + s++; + goto out; + + case '\\': + quote_char = '\\'; + break; + + default: + g_string_append_c (value, *s); + break; + } + } + else if (quote_char == '\\') + { + /* \ only counts as an escape if escaping a quote mark */ + if (*s != '\'') + g_string_append_c (value, '\\'); + + g_string_append_c (value, *s); + quote_char = 0; + } + else /* quote_char == ' */ + { + if (*s == '\'') + quote_char = 0; + else + g_string_append_c (value, *s); + } } - kdbus->priv->inited = TRUE; + out: + if (quote_char == '\\') + g_string_append_c (value, '\\'); + else if (quote_char == '\'') + { + g_string_free (value, TRUE); + return NULL; + } - return TRUE; + element->value = g_string_free (value, FALSE); + return s; } /** - * kdbus_source_prepare: + * match_new() * */ -static gboolean -kdbus_source_prepare (GSource *source, - gint *timeout) +static Match * +match_new (const char *str) { - GKdbusSource *kdbus_source = (GKdbusSource *)source; + Match *match; + GArray *elements; + const char *p; + const char *key_start; + const char *key_end; + MatchElement element; + int i; - if (g_cancellable_is_cancelled (kdbus_source->cancellable)) - return TRUE; + elements = g_array_new (TRUE, TRUE, sizeof (MatchElement)); - if (kdbus_source->timeout_time) - { - gint64 now; + p = str; - now = g_source_get_time (source); + while (*p != 0) + { + memset (&element, 0, sizeof (element)); - *timeout = (kdbus_source->timeout_time - now + 999) / 1000; - if (*timeout < 0) - { - kdbus_source->kdbus->priv->timed_out = TRUE; - *timeout = 0; - return TRUE; - } - } - else - *timeout = -1; + /* Skip initial whitespace */ + while (*p && g_ascii_isspace (*p)) + p++; - if ((kdbus_source->condition & kdbus_source->pollfd.revents) != 0) - return TRUE; + key_start = p; - return FALSE; -} + /* Read non-whitespace non-equals chars */ + while (*p && *p != '=' && !g_ascii_isspace (*p)) + p++; + key_end = p; -/** - * kdbus_source_check: - * - */ -static gboolean -kdbus_source_check (GSource *source) -{ - gint timeout; + /* Skip any whitespace after key */ + while (*p && g_ascii_isspace (*p)) + p++; - return kdbus_source_prepare (source, &timeout); -} + if (key_start == key_end) + continue; /* Allow trailing whitespace */ + if (*p != '=') + goto error; + ++p; -/** - * kdbus_source_dispatch - * - */ -static gboolean -kdbus_source_dispatch (GSource *source, - GSourceFunc callback, - gpointer user_data) -{ - GKdbusSourceFunc func = (GKdbusSourceFunc)callback; - GKdbusSource *kdbus_source = (GKdbusSource *)source; - GKdbus *kdbus = kdbus_source->kdbus; - gboolean ret; + if (!parse_key (&element, key_start, key_end)) + goto error; - if (kdbus_source->kdbus->priv->timed_out) - kdbus_source->pollfd.revents |= kdbus_source->condition & (G_IO_IN | G_IO_OUT); + p = parse_value (&element, p); + if (p == NULL) + goto error; - ret = (*func) (kdbus, - kdbus_source->pollfd.revents & kdbus_source->condition, - user_data); + g_array_append_val (elements, element); + } - if (kdbus->priv->timeout) - kdbus_source->timeout_time = g_get_monotonic_time () - + kdbus->priv->timeout * 1000000; + match = g_new0 (Match, 1); + match->n_elements = elements->len; + match->elements = (MatchElement *)g_array_free (elements, FALSE); - else - kdbus_source->timeout_time = 0; + return match; - return ret; + error: + for (i = 0; i < elements->len; i++) + g_free (g_array_index (elements, MatchElement, i).value); + g_array_free (elements, TRUE); + return NULL; } /** - * kdbus_source_finalize + * match_free() * */ static void -kdbus_source_finalize (GSource *source) +match_free (Match *match) { - GKdbusSource *kdbus_source = (GKdbusSource *)source; - GKdbus *kdbus; - - kdbus = kdbus_source->kdbus; - - g_object_unref (kdbus); - - if (kdbus_source->cancellable) - { - g_cancellable_release_fd (kdbus_source->cancellable); - g_object_unref (kdbus_source->cancellable); - } + int i; + for (i = 0; i < match->n_elements; i++) + g_free (match->elements[i].value); + g_free (match->elements); + g_free (match); } /** - * kdbus_source_closure_callback: + * g_kdbus_finalize: * */ -static gboolean -kdbus_source_closure_callback (GKdbus *kdbus, - GIOCondition condition, - gpointer data) +static void +g_kdbus_worker_finalize (GObject *object) { - GClosure *closure = data; - GValue params[2] = { G_VALUE_INIT, G_VALUE_INIT }; - GValue result_value = G_VALUE_INIT; - gboolean result; + GKDBusWorker *kdbus = G_KDBUS_WORKER (object); - g_value_init (&result_value, G_TYPE_BOOLEAN); + if (kdbus->kdbus_buffer != NULL) + munmap (kdbus->kdbus_buffer, KDBUS_POOL_SIZE); - g_value_init (¶ms[0], G_TYPE_KDBUS); - g_value_set_object (¶ms[0], kdbus); - g_value_init (¶ms[1], G_TYPE_IO_CONDITION); - g_value_set_flags (¶ms[1], condition); + kdbus->kdbus_buffer = NULL; - g_closure_invoke (closure, &result_value, 2, params, NULL); + if (kdbus->fd != -1 && !kdbus->closed) + _g_kdbus_close (kdbus); - result = g_value_get_boolean (&result_value); - g_value_unset (&result_value); - g_value_unset (¶ms[0]); - g_value_unset (¶ms[1]); - - return result; + G_OBJECT_CLASS (g_kdbus_worker_parent_class)->finalize (object); } - -static GSourceFuncs kdbus_source_funcs = +static void +g_kdbus_worker_class_init (GKDBusWorkerClass *class) { - kdbus_source_prepare, - kdbus_source_check, - kdbus_source_dispatch, - kdbus_source_finalize, - (GSourceFunc)kdbus_source_closure_callback, -}; - + class->finalize = g_kdbus_worker_finalize; +} -/** - * kdbus_source_new: - * - */ -static GSource * -kdbus_source_new (GKdbus *kdbus, - GIOCondition condition, - GCancellable *cancellable) +static void +g_kdbus_worker_init (GKDBusWorker *kdbus) { - GSource *source; - GKdbusSource *kdbus_source; - - source = g_source_new (&kdbus_source_funcs, sizeof (GKdbusSource)); - g_source_set_name (source, "GKdbus"); - kdbus_source = (GKdbusSource *)source; - - kdbus_source->kdbus = g_object_ref (kdbus); - kdbus_source->condition = condition; - - if (g_cancellable_make_pollfd (cancellable, - &kdbus_source->cancel_pollfd)) - { - kdbus_source->cancellable = g_object_ref (cancellable); - g_source_add_poll (source, &kdbus_source->cancel_pollfd); - } + kdbus->fd = -1; - kdbus_source->pollfd.fd = kdbus->priv->fd; - kdbus_source->pollfd.events = condition; - kdbus_source->pollfd.revents = 0; - g_source_add_poll (source, &kdbus_source->pollfd); + kdbus->unique_id = -1; + kdbus->unique_name = NULL; - if (kdbus->priv->timeout) - kdbus_source->timeout_time = g_get_monotonic_time () - + kdbus->priv->timeout * 1000000; - else - kdbus_source->timeout_time = 0; + kdbus->kdbus_buffer = NULL; - return source; + kdbus->flags = 0; /* KDBUS_HELLO_ACCEPT_FD */ + kdbus->attach_flags_send = _KDBUS_ATTACH_ALL; + kdbus->attach_flags_recv = _KDBUS_ATTACH_ALL; } - -/** - * _g_kdbus_create_source: - * - */ -GSource * -_g_kdbus_create_source (GKdbus *kdbus, - GIOCondition condition, - GCancellable *cancellable) +static gboolean +kdbus_ready (gint fd, + GIOCondition condition, + gpointer user_data) { - g_return_val_if_fail (G_IS_KDBUS (kdbus) && (cancellable == NULL || G_IS_CANCELLABLE (cancellable)), NULL); + GKDBusWorker *kdbus = user_data; + GError *error = NULL; - return kdbus_source_new (kdbus, condition, cancellable); -} + _g_kdbus_receive (kdbus, NULL, &error); + g_assert_no_error (error); + return G_SOURCE_CONTINUE; +} -/** - * _g_kdbus_open: - * - */ gboolean -_g_kdbus_open (GKdbus *kdbus, - const gchar *address, - GError **error) +_g_kdbus_open (GKDBusWorker *worker, + const gchar *address, + GError **error) { - g_return_val_if_fail (G_IS_KDBUS (kdbus), FALSE); + g_return_val_if_fail (G_IS_KDBUS_WORKER (worker), FALSE); - kdbus->priv->fd = open(address, O_RDWR|O_NOCTTY|O_CLOEXEC); - if (kdbus->priv->fd<0) + worker->fd = open(address, O_RDWR|O_NOCTTY|O_CLOEXEC); + if (worker->fd<0) { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, _("Can't open kdbus endpoint")); return FALSE; } - kdbus->priv->closed = FALSE; + worker->closed = FALSE; return TRUE; } @@ -445,16 +503,17 @@ _g_kdbus_open (GKdbus *kdbus, * */ static gboolean -g_kdbus_free_data (GKdbus *kdbus, +g_kdbus_free_data (GKDBusWorker *kdbus, guint64 offset) { - struct kdbus_cmd_free cmd; + struct kdbus_cmd_free cmd = { + .size = sizeof(cmd), + .offset = offset, + .flags = 0 + }; int ret; - cmd.offset = offset; - cmd.flags = 0; - - ret = ioctl (kdbus->priv->fd, KDBUS_CMD_FREE, &cmd); + ret = ioctl (kdbus->fd, KDBUS_CMD_FREE, &cmd); if (ret < 0) return FALSE; @@ -491,52 +550,36 @@ g_kdbus_translate_nameowner_flags (GBusNameOwnerFlags flags, * _g_kdbus_close: * */ -gboolean -_g_kdbus_close (GKdbus *kdbus, - GError **error) +void +_g_kdbus_close (GKDBusWorker *kdbus) { - gint res; + g_return_val_if_fail (G_IS_KDBUS_WORKER (kdbus), FALSE); - g_return_val_if_fail (G_IS_KDBUS (kdbus), FALSE); + if (kdbus->closed) + return; - if (kdbus->priv->closed) - return TRUE; - - while (1) - { - res = close (kdbus->priv->fd); - - if (res == -1) - { - if (errno == EINTR) - continue; + g_source_destroy (kdbus->source); + kdbus->source = 0; - g_set_error (error, G_IO_ERROR, - g_io_error_from_errno (errno), - _("Error closing kdbus fd: %s"), - g_strerror (errno)); - return FALSE; - } - break; - } + g_main_context_unref (kdbus->context); + kdbus->context = NULL; - kdbus->priv->closed = TRUE; - kdbus->priv->fd = -1; + close (kdbus->fd); + kdbus->fd = -1; - return TRUE; + kdbus->closed = TRUE; } - /** * _g_kdbus_is_closed: * */ gboolean -_g_kdbus_is_closed (GKdbus *kdbus) +_g_kdbus_is_closed (GKDBusWorker *kdbus) { - g_return_val_if_fail (G_IS_KDBUS (kdbus), FALSE); + g_return_val_if_fail (G_IS_KDBUS_WORKER (kdbus), FALSE); - return kdbus->priv->closed; + return kdbus->closed; } @@ -545,38 +588,35 @@ _g_kdbus_is_closed (GKdbus *kdbus) * */ GVariant * -_g_kdbus_Hello (GIOStream *stream, +_g_kdbus_Hello (GKDBusWorker *worker, GError **error) { - GKdbus *kdbus; - struct kdbus_cmd_hello *hello; + struct kdbus_cmd_hello *cmd; struct kdbus_item *item; gchar *conn_name; size_t size, conn_name_size; - kdbus = _g_kdbus_connection_get_kdbus (G_KDBUS_CONNECTION (stream)); - conn_name = "gdbus-kdbus"; conn_name_size = strlen (conn_name); size = KDBUS_ALIGN8 (G_STRUCT_OFFSET (struct kdbus_cmd_hello, items)) + KDBUS_ALIGN8 (G_STRUCT_OFFSET (struct kdbus_item, str) + conn_name_size + 1); - hello = g_alloca0 (size); - hello->flags = kdbus->priv->flags; - hello->attach_flags_send = kdbus->priv->attach_flags_send; - hello->attach_flags_recv = kdbus->priv->attach_flags_recv; - hello->size = size; - hello->pool_size = KDBUS_POOL_SIZE; + cmd = g_alloca0 (size); + cmd->flags = worker->flags; + cmd->attach_flags_send = worker->attach_flags_send; + cmd->attach_flags_recv = worker->attach_flags_recv; + cmd->size = size; + cmd->pool_size = KDBUS_POOL_SIZE; - item = hello->items; + item = cmd->items; item->size = G_STRUCT_OFFSET (struct kdbus_item, str) + conn_name_size + 1; item->type = KDBUS_ITEM_CONN_DESCRIPTION; memcpy (item->str, conn_name, conn_name_size+1); item = KDBUS_ITEM_NEXT (item); - if (ioctl(kdbus->priv->fd, KDBUS_CMD_HELLO, hello)) + if (ioctl(worker->fd, KDBUS_CMD_HELLO, cmd)) { g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errno), @@ -585,8 +625,8 @@ _g_kdbus_Hello (GIOStream *stream, return NULL; } - kdbus->priv->kdbus_buffer = mmap(NULL, KDBUS_POOL_SIZE, PROT_READ, MAP_SHARED, kdbus->priv->fd, 0); - if (kdbus->priv->kdbus_buffer == MAP_FAILED) + worker->kdbus_buffer = mmap(NULL, KDBUS_POOL_SIZE, PROT_READ, MAP_SHARED, worker->fd, 0); + if (worker->kdbus_buffer == MAP_FAILED) { g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errno), @@ -595,7 +635,7 @@ _g_kdbus_Hello (GIOStream *stream, return NULL; } - if (hello->bus_flags > 0xFFFFFFFFULL) + if (cmd->bus_flags > 0xFFFFFFFFULL) { g_set_error_literal (error, G_IO_ERROR, @@ -604,44 +644,37 @@ _g_kdbus_Hello (GIOStream *stream, return NULL; } - memcpy (kdbus->priv->bus_id, hello->id128, 16); + memcpy (worker->bus_id, cmd->id128, 16); + + worker->unique_id = cmd->id; + asprintf(&worker->unique_name, ":1.%llu", (unsigned long long) cmd->id); - kdbus->priv->unique_id = hello->id; - asprintf(&kdbus->priv->unique_name, ":1.%llu", (unsigned long long) hello->id); + /* read bloom filters parameters */ + //worker->bloom_size = (gsize) cmd->bloom.size; + //worker->bloom_n_hash = (guint) cmd->bloom.n_hash; - return g_variant_new ("(s)", kdbus->priv->unique_name); + return g_variant_new ("(s)", worker->unique_name); } -/* +/** * _g_kdbus_RequestName: * */ GVariant * -_g_kdbus_RequestName (GDBusConnection *connection, +_g_kdbus_RequestName (GKDBusWorker *worker, const gchar *name, GBusNameOwnerFlags flags, GError **error) { - GKdbus *kdbus; GVariant *result; - struct kdbus_cmd_name *kdbus_name; + struct kdbus_cmd *cmd; guint64 kdbus_flags; gssize len, size; gint status, ret; status = G_BUS_REQUEST_NAME_FLAGS_PRIMARY_OWNER; - kdbus = _g_kdbus_connection_get_kdbus (G_KDBUS_CONNECTION (g_dbus_connection_get_stream (connection))); - if (kdbus == NULL) - { - g_set_error_literal (error, - G_DBUS_ERROR, - G_DBUS_ERROR_IO_ERROR, - _("The connection is closed")); - return NULL; - } - if (!g_dbus_is_name (name)) { g_set_error (error, @@ -663,15 +696,15 @@ _g_kdbus_RequestName (GDBusConnection *connection, g_kdbus_translate_nameowner_flags (flags, &kdbus_flags); len = strlen(name) + 1; - size = G_STRUCT_OFFSET (struct kdbus_cmd_name, items) + KDBUS_ITEM_SIZE(len); - kdbus_name = g_alloca0 (size); - kdbus_name->size = size; - kdbus_name->items[0].size = KDBUS_ITEM_HEADER_SIZE + len; - kdbus_name->items[0].type = KDBUS_ITEM_NAME; - kdbus_name->flags = kdbus_flags; - memcpy (kdbus_name->items[0].str, name, len); - - ret = ioctl(kdbus->priv->fd, KDBUS_CMD_NAME_ACQUIRE, kdbus_name); + size = G_STRUCT_OFFSET (struct kdbus_cmd, items) + KDBUS_ITEM_SIZE(len); + cmd = g_alloca0 (size); + cmd->size = size; + cmd->items[0].size = KDBUS_ITEM_HEADER_SIZE + len; + cmd->items[0].type = KDBUS_ITEM_NAME; + cmd->flags = kdbus_flags; + memcpy (cmd->items[0].str, name, len); + + ret = ioctl(worker->fd, KDBUS_CMD_NAME_ACQUIRE, cmd); if (ret < 0) { if (errno == EEXIST) @@ -688,7 +721,7 @@ _g_kdbus_RequestName (GDBusConnection *connection, } } - if (kdbus_name->flags & KDBUS_NAME_IN_QUEUE) + if (cmd->flags & KDBUS_NAME_IN_QUEUE) status = G_BUS_REQUEST_NAME_FLAGS_IN_QUEUE; result = g_variant_new ("(u)", status); @@ -697,33 +730,22 @@ _g_kdbus_RequestName (GDBusConnection *connection, } -/* +/** * _g_kdbus_ReleaseName: * */ GVariant * -_g_kdbus_ReleaseName (GDBusConnection *connection, +_g_kdbus_ReleaseName (GKDBusWorker *worker, const gchar *name, GError **error) { - GKdbus *kdbus; GVariant *result; - struct kdbus_cmd_name *kdbus_name; + struct kdbus_cmd *cmd; gssize len, size; gint status, ret; status = G_BUS_RELEASE_NAME_FLAGS_RELEASED; - kdbus = _g_kdbus_connection_get_kdbus (G_KDBUS_CONNECTION (g_dbus_connection_get_stream (connection))); - if (kdbus == NULL) - { - g_set_error_literal (error, - G_DBUS_ERROR, - G_DBUS_ERROR_IO_ERROR, - _("The connection is closed")); - return NULL; - } - if (!g_dbus_is_name (name)) { g_set_error (error, @@ -743,14 +765,14 @@ _g_kdbus_ReleaseName (GDBusConnection *connection, } len = strlen(name) + 1; - size = G_STRUCT_OFFSET (struct kdbus_cmd_name, items) + KDBUS_ITEM_SIZE(len); - kdbus_name = g_alloca0 (size); - kdbus_name->size = size; - kdbus_name->items[0].size = KDBUS_ITEM_HEADER_SIZE + len; - kdbus_name->items[0].type = KDBUS_ITEM_NAME; - memcpy (kdbus_name->items[0].str, name, len); - - ret = ioctl(kdbus->priv->fd, KDBUS_CMD_NAME_RELEASE, kdbus_name); + size = G_STRUCT_OFFSET (struct kdbus_cmd, items) + KDBUS_ITEM_SIZE(len); + cmd = g_alloca0 (size); + cmd->size = size; + cmd->items[0].size = KDBUS_ITEM_HEADER_SIZE + len; + cmd->items[0].type = KDBUS_ITEM_NAME; + memcpy (cmd->items[0].str, name, len); + + ret = ioctl(worker->fd, KDBUS_CMD_NAME_RELEASE, cmd); if (ret < 0) { if (errno == ESRCH) @@ -778,28 +800,17 @@ _g_kdbus_ReleaseName (GDBusConnection *connection, * */ GVariant * -_g_kdbus_GetBusId (GDBusConnection *connection, +_g_kdbus_GetBusId (GKDBusWorker *worker, GError **error) { - GKdbus *kdbus; GVariant *result; GString *result_str; guint cnt; result_str = g_string_new (NULL); - kdbus = _g_kdbus_connection_get_kdbus (G_KDBUS_CONNECTION (g_dbus_connection_get_stream (connection))); - if (kdbus == NULL) - { - g_set_error_literal (error, - G_DBUS_ERROR, - G_DBUS_ERROR_IO_ERROR, - _("The connection is closed")); - g_string_free (result_str, TRUE); - return NULL; - } for (cnt=0; cnt<16; cnt++) - g_string_append_printf (result_str, "%02x", kdbus->priv->bus_id[cnt]); + g_string_append_printf (result_str, "%02x", worker->bus_id[cnt]); result = g_variant_new ("(s)", result_str->str); g_string_free (result_str, TRUE); @@ -813,38 +824,28 @@ _g_kdbus_GetBusId (GDBusConnection *connection, * */ GVariant * -_g_kdbus_GetListNames (GDBusConnection *connection, +_g_kdbus_GetListNames (GKDBusWorker *worker, guint list_name_type, GError **error) { - GKdbus *kdbus; GVariant *result; GVariantBuilder *builder; - - struct kdbus_cmd_name_list cmd = {}; - struct kdbus_name_list *name_list; - struct kdbus_name_info *name; + struct kdbus_info *name_list, *name; + struct kdbus_cmd_list cmd = { + .size = sizeof(cmd) + }; guint64 prev_id; gint ret; prev_id = 0; - kdbus = _g_kdbus_connection_get_kdbus (G_KDBUS_CONNECTION (g_dbus_connection_get_stream (connection))); - if (kdbus == NULL) - { - g_set_error_literal (error, - G_DBUS_ERROR, - G_DBUS_ERROR_IO_ERROR, - _("The connection is closed")); - return NULL; - } if (list_name_type) - cmd.flags = KDBUS_NAME_LIST_ACTIVATORS; /* ListActivatableNames */ + cmd.flags = KDBUS_LIST_ACTIVATORS; /* ListActivatableNames */ else - cmd.flags = KDBUS_NAME_LIST_UNIQUE | KDBUS_NAME_LIST_NAMES; /* ListNames */ + cmd.flags = KDBUS_LIST_UNIQUE | KDBUS_LIST_NAMES; /* ListNames */ - ret = ioctl(kdbus->priv->fd, KDBUS_CMD_NAME_LIST, &cmd); + ret = ioctl(worker->fd, KDBUS_CMD_LIST, &cmd); if (ret < 0) { g_set_error (error, @@ -854,23 +855,23 @@ _g_kdbus_GetListNames (GDBusConnection *connection, return NULL; } - name_list = (struct kdbus_name_list *) ((guint8 *) kdbus->priv->kdbus_buffer + cmd.offset); + name_list = (struct kdbus_info *) ((guint8 *) worker->kdbus_buffer + cmd.offset); builder = g_variant_builder_new (G_VARIANT_TYPE ("as")); - KDBUS_ITEM_FOREACH(name, name_list, names) + KDBUS_FOREACH(name, name_list, cmd.list_size) { struct kdbus_item *item; const gchar *item_name = ""; - if ((cmd.flags & KDBUS_NAME_LIST_UNIQUE) && name->owner_id != prev_id) + if ((cmd.flags & KDBUS_LIST_UNIQUE) && name->id != prev_id) { GString *unique_name; unique_name = g_string_new (NULL); - g_string_printf (unique_name, ":1.%llu", name->owner_id); + g_string_printf (unique_name, ":1.%llu", name->id); g_variant_builder_add (builder, "s", unique_name->str); g_string_free (unique_name,TRUE); - prev_id = name->owner_id; + prev_id = name->id; } KDBUS_ITEM_FOREACH(item, name, items) @@ -884,7 +885,7 @@ _g_kdbus_GetListNames (GDBusConnection *connection, result = g_variant_new ("(as)", builder); g_variant_builder_unref (builder); - g_kdbus_free_data (kdbus, cmd.offset); + g_kdbus_free_data (worker, cmd.offset); return result; } @@ -894,7 +895,7 @@ _g_kdbus_GetListNames (GDBusConnection *connection, * */ static gboolean -g_kdbus_NameHasOwner_internal (GKdbus *kdbus, +g_kdbus_NameHasOwner_internal (GKDBusWorker *worker, const gchar *name, GError **error) { @@ -919,8 +920,8 @@ g_kdbus_NameHasOwner_internal (GKdbus *kdbus, } cmd->size = size; - ret = ioctl(kdbus->priv->fd, KDBUS_CMD_CONN_INFO, cmd); - g_kdbus_free_data (kdbus, cmd->offset); + ret = ioctl(worker->fd, KDBUS_CMD_CONN_INFO, cmd); + g_kdbus_free_data (worker, cmd->offset); if (ret < 0) return FALSE; @@ -934,29 +935,20 @@ g_kdbus_NameHasOwner_internal (GKdbus *kdbus, * */ GVariant * -_g_kdbus_GetListQueuedOwners (GDBusConnection *connection, - const gchar *name, - GError **error) +_g_kdbus_GetListQueuedOwners (GKDBusWorker *worker, + const gchar *name, + GError **error) { - GKdbus *kdbus; GVariant *result; GVariantBuilder *builder; GString *unique_name; gint ret; - struct kdbus_cmd_name_list cmd = {}; - struct kdbus_name_list *name_list; - struct kdbus_name_info *kname; - - kdbus = _g_kdbus_connection_get_kdbus (G_KDBUS_CONNECTION (g_dbus_connection_get_stream (connection))); - if (kdbus == NULL) - { - g_set_error_literal (error, - G_DBUS_ERROR, - G_DBUS_ERROR_IO_ERROR, - _("The connection is closed")); - return NULL; - } + struct kdbus_info *name_list, *kname; + struct kdbus_cmd_list cmd = { + .size = sizeof(cmd), + .flags = KDBUS_LIST_QUEUED + }; if (!g_dbus_is_name (name)) { @@ -967,7 +959,7 @@ _g_kdbus_GetListQueuedOwners (GDBusConnection *connection, return NULL; } - if (!g_kdbus_NameHasOwner_internal (kdbus, name, error)) + if (!g_kdbus_NameHasOwner_internal (worker, name, error)) { g_set_error (error, G_DBUS_ERROR, @@ -976,8 +968,7 @@ _g_kdbus_GetListQueuedOwners (GDBusConnection *connection, return NULL; } - cmd.flags = KDBUS_NAME_LIST_QUEUED; - ret = ioctl(kdbus->priv->fd, KDBUS_CMD_NAME_LIST, &cmd); + ret = ioctl(worker->fd, KDBUS_CMD_LIST, &cmd); if (ret < 0) { g_set_error (error, @@ -987,11 +978,11 @@ _g_kdbus_GetListQueuedOwners (GDBusConnection *connection, return NULL; } - name_list = (struct kdbus_name_list *) ((guint8 *) kdbus->priv->kdbus_buffer + cmd.offset); + name_list = (struct kdbus_info *) ((guint8 *) worker->kdbus_buffer + cmd.offset); unique_name = g_string_new (NULL); builder = g_variant_builder_new (G_VARIANT_TYPE ("as")); - KDBUS_ITEM_FOREACH(kname, name_list, names) + KDBUS_FOREACH(kname, name_list, cmd.list_size) { struct kdbus_item *item; const char *item_name = ""; @@ -1003,7 +994,7 @@ _g_kdbus_GetListQueuedOwners (GDBusConnection *connection, if (strcmp(item_name, name)) continue; - g_string_printf (unique_name, ":1.%llu", kname->owner_id); + g_string_printf (unique_name, ":1.%llu", kname->id); g_variant_builder_add (builder, "s", item_name); } @@ -1011,7 +1002,7 @@ _g_kdbus_GetListQueuedOwners (GDBusConnection *connection, g_variant_builder_unref (builder); g_string_free (unique_name,TRUE); - g_kdbus_free_data (kdbus, cmd.offset); + g_kdbus_free_data (worker, cmd.offset); return result; } @@ -1021,12 +1012,11 @@ _g_kdbus_GetListQueuedOwners (GDBusConnection *connection, * */ static GVariant * -g_kdbus_GetConnInfo_internal (GDBusConnection *connection, +g_kdbus_GetConnInfo_internal (GKDBusWorker *worker, const gchar *name, guint64 flag, GError **error) { - GKdbus *kdbus; GVariant *result; struct kdbus_cmd_info *cmd; @@ -1036,15 +1026,6 @@ g_kdbus_GetConnInfo_internal (GDBusConnection *connection, gint ret; result = NULL; - kdbus = _g_kdbus_connection_get_kdbus (G_KDBUS_CONNECTION (g_dbus_connection_get_stream (connection))); - if (kdbus == NULL) - { - g_set_error_literal (error, - G_DBUS_ERROR, - G_DBUS_ERROR_IO_ERROR, - _("The connection is closed")); - return NULL; - } if (!g_dbus_is_name (name)) { @@ -1055,7 +1036,7 @@ g_kdbus_GetConnInfo_internal (GDBusConnection *connection, return NULL; } - if (!g_kdbus_NameHasOwner_internal (kdbus, name, error)) + if (!g_kdbus_NameHasOwner_internal (worker, name, error)) { g_set_error (error, G_DBUS_ERROR, @@ -1083,7 +1064,7 @@ g_kdbus_GetConnInfo_internal (GDBusConnection *connection, cmd->flags = _KDBUS_ATTACH_ALL; cmd->size = size; - ret = ioctl(kdbus->priv->fd, KDBUS_CMD_CONN_INFO, cmd); + ret = ioctl(worker->fd, KDBUS_CMD_CONN_INFO, cmd); if (ret < 0) { g_set_error (error, @@ -1093,7 +1074,7 @@ g_kdbus_GetConnInfo_internal (GDBusConnection *connection, return NULL; } - conn_info = (struct kdbus_info *) ((guint8 *) kdbus->priv->kdbus_buffer + cmd->offset); + conn_info = (struct kdbus_info *) ((guint8 *) worker->kdbus_buffer + cmd->offset); /* if (conn_info->flags & KDBUS_HELLO_ACTIVATOR) @@ -1115,14 +1096,16 @@ g_kdbus_GetConnInfo_internal (GDBusConnection *connection, { switch (item->type) { - case KDBUS_ITEM_CREDS: + case KDBUS_ITEM_PIDS: if (flag == G_BUS_CREDS_PID) { - guint pid = item->creds.pid; + guint pid = item->pids.pid; result = g_variant_new ("(u)", pid); goto exit; - } + } + + case KDBUS_ITEM_CREDS: if (flag == G_BUS_CREDS_UID) { @@ -1138,14 +1121,16 @@ g_kdbus_GetConnInfo_internal (GDBusConnection *connection, case KDBUS_ITEM_CMDLINE: case KDBUS_ITEM_CGROUP: case KDBUS_ITEM_CAPS: - case KDBUS_ITEM_NAME: case KDBUS_ITEM_AUDIT: + case KDBUS_ITEM_CONN_DESCRIPTION: + case KDBUS_ITEM_AUXGROUPS: + case KDBUS_ITEM_OWNED_NAME: break; } } exit: - g_kdbus_free_data (kdbus, cmd->offset); + g_kdbus_free_data (worker, cmd->offset); return result; } @@ -1155,11 +1140,11 @@ exit: * */ GVariant * -_g_kdbus_GetNameOwner (GDBusConnection *connection, +_g_kdbus_GetNameOwner (GKDBusWorker *worker, const gchar *name, GError **error) { - return g_kdbus_GetConnInfo_internal (connection, + return g_kdbus_GetConnInfo_internal (worker, name, G_BUS_CREDS_UNIQUE_NAME, error); @@ -1171,11 +1156,11 @@ _g_kdbus_GetNameOwner (GDBusConnection *connection, * */ GVariant * -_g_kdbus_GetConnectionUnixProcessID (GDBusConnection *connection, +_g_kdbus_GetConnectionUnixProcessID (GKDBusWorker *worker, const gchar *name, GError **error) { - return g_kdbus_GetConnInfo_internal (connection, + return g_kdbus_GetConnInfo_internal (worker, name, G_BUS_CREDS_PID, error); @@ -1187,88 +1172,320 @@ _g_kdbus_GetConnectionUnixProcessID (GDBusConnection *connection, * */ GVariant * -_g_kdbus_GetConnectionUnixUser (GDBusConnection *connection, +_g_kdbus_GetConnectionUnixUser (GKDBusWorker *worker, const gchar *name, GError **error) { - return g_kdbus_GetConnInfo_internal (connection, + return g_kdbus_GetConnInfo_internal (worker, name, G_BUS_CREDS_UID, error); } -/* - * _g_kdbus_match_remove: - * +/** + * g_kdbus_bloom_add_data: + * Based on bus-bloom.c from systemd + * http://cgit.freedesktop.org/systemd/systemd/tree/src/libsystemd/sd-bus/bus-bloom.c */ static void -_g_kdbus_match_remove (GDBusConnection *connection, - guint cookie) +g_kdbus_bloom_add_data (GKDBusWorker *worker, + guint64 bloom_data [], + const void *data, + gsize n) { - GKdbus *kdbus; - struct kdbus_cmd_match cmd_match = {}; - gint ret; + guint8 hash[8]; + guint64 bit_num; + guint bytes_num = 0; + guint cnt_1, cnt_2; - kdbus = _g_kdbus_connection_get_kdbus (G_KDBUS_CONNECTION (g_dbus_connection_get_stream (connection))); + guint c = 0; + guint64 p = 0; - cmd_match.size = sizeof (cmd_match); - cmd_match.cookie = cookie; + bit_num = worker->bloom_size * 8; - ret = ioctl(kdbus->priv->fd, KDBUS_CMD_MATCH_REMOVE, &cmd_match); - if (ret < 0) - g_warning ("ERROR - %d\n", (int) errno); + if (bit_num > 1) + bytes_num = ((__builtin_clzll(bit_num) ^ 63U) + 7) / 8; + + for (cnt_1 = 0; cnt_1 < (worker->bloom_n_hash); cnt_1++) + { + for (cnt_2 = 0; cnt_2 < bytes_num; cnt_2++) + { + if (c <= 0) + { + g_siphash24(hash, data, n, hash_keys[cnt_1++]); + c += 8; + } + + p = (p << 8ULL) | (guint64) hash[8 - c]; + c--; + } + + p &= bit_num - 1; + bloom_data[p >> 6] |= 1ULL << (p & 63); + } } -/* - * _g_kdbus_subscribe_name_acquired: +/** + * g_kdbus_bloom_add_pair: * */ static void -_g_kdbus_subscribe_name_owner_changed (GDBusConnection *connection, - const gchar *name, - const gchar *old_name, - const gchar *new_name, - guint cookie) +g_kdbus_bloom_add_pair (GKDBusWorker *worker, + guint64 bloom_data [], + const gchar *parameter, + const gchar *value) { - GKdbus *kdbus; - struct kdbus_item *item; - struct kdbus_cmd_match *cmd_match; - gssize size, len; - gint ret; - guint64 old_id; - guint64 new_id = KDBUS_MATCH_ID_ANY; + gchar buf[1024]; + gsize size; - kdbus = _g_kdbus_connection_get_kdbus (G_KDBUS_CONNECTION (g_dbus_connection_get_stream (connection))); + size = strlen(parameter) + strlen(value) + 1; + if (size > 1024) + return; - len = strlen(name) + 1; - size = KDBUS_ALIGN8(G_STRUCT_OFFSET (struct kdbus_cmd_match, items) + - G_STRUCT_OFFSET (struct kdbus_item, name_change) + - G_STRUCT_OFFSET (struct kdbus_notify_name_change, name) + len); + strcpy(stpcpy(stpcpy(buf, parameter), ":"), value); + g_kdbus_bloom_add_data(worker, bloom_data, buf, size); +} - cmd_match = g_alloca0 (size); - cmd_match->size = size; - cmd_match->cookie = cookie; - item = cmd_match->items; - if (old_name[0] == 0) - { - old_id = KDBUS_MATCH_ID_ANY; - } - else +/** + * g_kdbus_bloom_add_prefixes: + * + */ +static void +g_kdbus_bloom_add_prefixes (GKDBusWorker *worker, + guint64 bloom_data [], + const gchar *parameter, + const gchar *value, + gchar separator) +{ + gchar buf[1024]; + gsize size; + + size = strlen(parameter) + strlen(value) + 1; + if (size > 1024) + return; + + strcpy(stpcpy(stpcpy(buf, parameter), ":"), value); + + for (;;) { - if (g_dbus_is_unique_name(old_name)) - old_id = old_id+3; - else - return; + gchar *last_sep; + last_sep = strrchr(buf, separator); + if (!last_sep || last_sep == buf) + break; + + *last_sep = 0; + g_kdbus_bloom_add_data(worker, bloom_data, buf, last_sep-buf); } +} - if (new_name[0] == 0) + +/** + * _g_kdbus_AddMatch: + * + */ +void +_g_kdbus_AddMatch (GKDBusWorker *worker, + const gchar *match_rule, + guint cookie) +{ + Match *match; + MatchElement *element; + const gchar *sender_name; + gsize sender_len, size; + struct kdbus_cmd_match *cmd; + struct kdbus_item *item; + guint64 *bloom; + guint64 src_id; + gchar *type; + gint cnt, ret; + + if (match_rule[0] == '-') + return; + + match = match_new (match_rule); + if (!match) { - new_id = KDBUS_MATCH_ID_ANY; + match_free (match); + return; } - else + + sender_name = NULL; + src_id = KDBUS_MATCH_ID_ANY; + + bloom = g_alloca0 (worker->bloom_size); + size = KDBUS_ALIGN8 (G_STRUCT_OFFSET (struct kdbus_cmd_match, items)); + match = match_new (match_rule); + + for (cnt = 0; cnt < match->n_elements; cnt++) + { + element = &match->elements[cnt]; + switch (element->type) + { + case MATCH_ELEMENT_SENDER: + if (g_dbus_is_unique_name(element->value)) + { + src_id = g_ascii_strtoull ((element->value)+3, NULL, 10); + size += KDBUS_ALIGN8 (G_STRUCT_OFFSET (struct kdbus_item, id) + sizeof(src_id)); + } + else if (g_dbus_is_name (element->value)) + { + sender_name = element->value; + sender_len = strlen(element->value) + 1; + size += KDBUS_ALIGN8 (G_STRUCT_OFFSET (struct kdbus_item, str) + sender_len); + } + else + { + g_critical ("Error while adding a match: %d", cookie); + match_free (match); + return; + } + break; + + case MATCH_ELEMENT_TYPE: + g_kdbus_bloom_add_pair (worker, bloom, "message-type", element->value); + break; + + case MATCH_ELEMENT_INTERFACE: + g_kdbus_bloom_add_pair (worker, bloom, "interface", element->value); + break; + + case MATCH_ELEMENT_MEMBER: + g_kdbus_bloom_add_pair (worker, bloom, "member", element->value); + break; + + case MATCH_ELEMENT_PATH: + g_kdbus_bloom_add_pair (worker, bloom, "path", element->value); + break; + + case MATCH_ELEMENT_PATH_NAMESPACE: + if (g_strcmp0 (element->value, "/")) + g_kdbus_bloom_add_pair (worker, bloom, "path-slash-prefix", element->value); + break; + + case MATCH_ELEMENT_ARGN: + asprintf (&type, "arg%u", element->arg); + g_kdbus_bloom_add_pair (worker, bloom, type, element->value); + free (type); + break; + + case MATCH_ELEMENT_ARGNPATH: + asprintf (&type, "arg%u-slash-prefix", element->arg); + g_kdbus_bloom_add_pair (worker, bloom, type, element->value); + free (type); + break; + + case MATCH_ELEMENT_ARG0NAMESPACE: + g_kdbus_bloom_add_pair (worker, bloom, "arg0-dot-prefix", element->value); + break; + + case MATCH_ELEMENT_DESTINATION: + case MATCH_ELEMENT_EAVESDROP: + break; + } + } + + size += KDBUS_ALIGN8 (G_STRUCT_OFFSET (struct kdbus_item, data64) + worker->bloom_size); + cmd = g_alloca0 (size); + cmd->size = size; + cmd->cookie = cookie; + + item = cmd->items; + item->size = G_STRUCT_OFFSET(struct kdbus_item, data64) + worker->bloom_size; + item->type = KDBUS_ITEM_BLOOM_MASK; + memcpy(item->data64, bloom, worker->bloom_size); + item = KDBUS_ITEM_NEXT(item); + + if (src_id != KDBUS_MATCH_ID_ANY) + { + item->size = G_STRUCT_OFFSET (struct kdbus_item, id) + sizeof(src_id); + item->type = KDBUS_ITEM_ID; + item->id = src_id; + item = KDBUS_ITEM_NEXT(item); + } + + if (sender_name) + { + item->size = G_STRUCT_OFFSET (struct kdbus_item, str) + sender_len; + item->type = KDBUS_ITEM_NAME; + memcpy (item->str, sender_name, sender_len); + } + + ret = ioctl(worker->fd, KDBUS_CMD_MATCH_ADD, cmd); + if (ret < 0) + g_critical ("Error while adding a match: %d", cookie); + + match_free (match); +} + + +/** + * _g_kdbus_RemoveMatch: + * + */ +void +_g_kdbus_RemoveMatch (GKDBusWorker *worker, + guint cookie) +{ + struct kdbus_cmd_match cmd = { + .size = sizeof(cmd), + .cookie = cookie + }; + gint ret; + + ret = ioctl(worker->fd, KDBUS_CMD_MATCH_REMOVE, &cmd); + if (ret < 0) + g_warning ("Error while removing a match: %d\n", (int) errno); +} + + +/** + * _g_kdbus_subscribe_name_acquired: + * + */ +static void +_g_kdbus_subscribe_name_owner_changed (GKDBusWorker *worker, + const gchar *name, + const gchar *old_name, + const gchar *new_name, + guint cookie) +{ + struct kdbus_item *item; + struct kdbus_cmd_match *cmd; + gssize size, len; + gint ret; + guint64 old_id = 0; /* XXX why? */ + guint64 new_id = KDBUS_MATCH_ID_ANY; + + len = strlen(name) + 1; + size = KDBUS_ALIGN8(G_STRUCT_OFFSET (struct kdbus_cmd_match, items) + + G_STRUCT_OFFSET (struct kdbus_item, name_change) + + G_STRUCT_OFFSET (struct kdbus_notify_name_change, name) + len); + + cmd = g_alloca0 (size); + cmd->size = size; + cmd->cookie = cookie; + item = cmd->items; + + if (old_name[0] == 0) + { + old_id = KDBUS_MATCH_ID_ANY; + } + else + { + if (g_dbus_is_unique_name(old_name)) + old_id = old_id+3; + else + return; + } + + if (new_name[0] == 0) + { + new_id = KDBUS_MATCH_ID_ANY; + } + else { if (g_dbus_is_unique_name(new_name)) new_id = new_id+3; @@ -1276,10 +1493,10 @@ _g_kdbus_subscribe_name_owner_changed (GDBusConnection *connection, return; } - cmd_match = g_alloca0 (size); - cmd_match->size = size; - cmd_match->cookie = cookie; - item = cmd_match->items; + cmd = g_alloca0 (size); + cmd->size = size; + cmd->cookie = cookie; + item = cmd->items; /* KDBUS_ITEM_NAME_CHANGE */ item->type = KDBUS_ITEM_NAME_CHANGE; @@ -1290,141 +1507,261 @@ _g_kdbus_subscribe_name_owner_changed (GDBusConnection *connection, G_STRUCT_OFFSET(struct kdbus_notify_name_change, name) + len; item = KDBUS_ITEM_NEXT(item); - ret = ioctl(kdbus->priv->fd, KDBUS_CMD_MATCH_ADD, cmd_match); + ret = ioctl(worker->fd, KDBUS_CMD_MATCH_ADD, cmd); if (ret < 0) g_warning ("ERROR - %d\n", (int) errno); } -/* +/** * _g_kdbus_subscribe_name_acquired: * */ void -_g_kdbus_subscribe_name_acquired (GDBusConnection *connection, +_g_kdbus_subscribe_name_acquired (GKDBusWorker *worker, const gchar *name) { - GKdbus *kdbus; struct kdbus_item *item; - struct kdbus_cmd_match *cmd_match; + struct kdbus_cmd_match *cmd; gssize size, len; guint64 cookie; gint ret; - kdbus = _g_kdbus_connection_get_kdbus (G_KDBUS_CONNECTION (g_dbus_connection_get_stream (connection))); - len = strlen(name) + 1; size = KDBUS_ALIGN8(G_STRUCT_OFFSET (struct kdbus_cmd_match, items) + G_STRUCT_OFFSET (struct kdbus_item, name_change) + G_STRUCT_OFFSET (struct kdbus_notify_name_change, name) + len); cookie = 0xbeefbeefbeefbeef; - cmd_match = g_alloca0 (size); - cmd_match->size = size; - cmd_match->cookie = cookie; - item = cmd_match->items; + cmd = g_alloca0 (size); + cmd->size = size; + cmd->cookie = cookie; + item = cmd->items; /* KDBUS_ITEM_NAME_ADD */ item->type = KDBUS_ITEM_NAME_ADD; item->name_change.old_id.id = KDBUS_MATCH_ID_ANY; - item->name_change.new_id.id = kdbus->priv->unique_id; + item->name_change.new_id.id = worker->unique_id; memcpy(item->name_change.name, name, len); item->size = G_STRUCT_OFFSET (struct kdbus_item, name_change) + G_STRUCT_OFFSET(struct kdbus_notify_name_change, name) + len; item = KDBUS_ITEM_NEXT(item); - ret = ioctl(kdbus->priv->fd, KDBUS_CMD_MATCH_ADD, cmd_match); + ret = ioctl(worker->fd, KDBUS_CMD_MATCH_ADD, cmd); if (ret < 0) g_warning ("ERROR - %d\n", (int) errno); - _g_kdbus_subscribe_name_owner_changed (connection, name, "", kdbus->priv->unique_name, cookie); + _g_kdbus_subscribe_name_owner_changed (worker, name, "", worker->unique_name, cookie); } -/* +/** * _g_kdbus_subscribe_name_lost: * */ void -_g_kdbus_subscribe_name_lost (GDBusConnection *connection, +_g_kdbus_subscribe_name_lost (GKDBusWorker *worker, const gchar *name) { - GKdbus *kdbus; struct kdbus_item *item; - struct kdbus_cmd_match *cmd_match; + struct kdbus_cmd_match *cmd; gssize size, len; guint64 cookie; gint ret; - kdbus = _g_kdbus_connection_get_kdbus (G_KDBUS_CONNECTION (g_dbus_connection_get_stream (connection))); - len = strlen(name) + 1; size = KDBUS_ALIGN8(G_STRUCT_OFFSET (struct kdbus_cmd_match, items) + G_STRUCT_OFFSET (struct kdbus_item, name_change) + G_STRUCT_OFFSET (struct kdbus_notify_name_change, name) + len); cookie = 0xdeafdeafdeafdeaf; - cmd_match = g_alloca0 (size); - cmd_match->size = size; - cmd_match->cookie = cookie; - item = cmd_match->items; + cmd = g_alloca0 (size); + cmd->size = size; + cmd->cookie = cookie; + item = cmd->items; /* KDBUS_ITEM_NAME_REMOVE */ item->type = KDBUS_ITEM_NAME_REMOVE; - item->name_change.old_id.id = kdbus->priv->unique_id; + item->name_change.old_id.id = worker->unique_id; item->name_change.new_id.id = KDBUS_MATCH_ID_ANY; memcpy(item->name_change.name, name, len); item->size = G_STRUCT_OFFSET (struct kdbus_item, name_change) + G_STRUCT_OFFSET(struct kdbus_notify_name_change, name) + len; item = KDBUS_ITEM_NEXT(item); - ret = ioctl(kdbus->priv->fd, KDBUS_CMD_MATCH_ADD, cmd_match); + ret = ioctl(worker->fd, KDBUS_CMD_MATCH_ADD, cmd); if (ret < 0) g_warning ("ERROR - %d\n", (int) errno); - _g_kdbus_subscribe_name_owner_changed (connection, name, kdbus->priv->unique_name, "", cookie); + _g_kdbus_subscribe_name_owner_changed (worker, name, worker->unique_name, "", cookie); } -/* +/** * _g_kdbus_unsubscribe_name_acquired: * */ void -_g_kdbus_unsubscribe_name_acquired (GDBusConnection *connection) +_g_kdbus_unsubscribe_name_acquired (GKDBusWorker *worker) { guint64 cookie; cookie = 0xbeefbeefbeefbeef; - _g_kdbus_match_remove (connection, cookie); + _g_kdbus_RemoveMatch (worker, cookie); } -/* +/** * _g_kdbus_unsubscribe_name_lost: * */ void -_g_kdbus_unsubscribe_name_lost (GDBusConnection *connection) +_g_kdbus_unsubscribe_name_lost (GKDBusWorker *worker) { guint64 cookie; cookie = 0xdeafdeafdeafdeaf; - _g_kdbus_match_remove (connection, cookie); + _g_kdbus_RemoveMatch (worker, cookie); } /** -* g_kdbus_decode_kernel_msg: -* -*/ -static gssize -g_kdbus_decode_kernel_msg (GKdbus *kdbus) + * g_kdbus_append_payload_bloom: + * + */ +static struct kdbus_bloom_filter * +g_kdbus_append_bloom (struct kdbus_item **item, + gsize size) +{ + struct kdbus_item *bloom_item; + + bloom_item = KDBUS_ALIGN8_PTR(*item); + bloom_item->size = G_STRUCT_OFFSET (struct kdbus_item, bloom_filter) + + G_STRUCT_OFFSET (struct kdbus_bloom_filter, data) + + size; + + bloom_item->type = KDBUS_ITEM_BLOOM_FILTER; + + *item = KDBUS_ITEM_NEXT(bloom_item); + return &bloom_item->bloom_filter; +} + + +/** + * g_kdbus_setup_bloom: + * Based on bus-bloom.c from systemd + * http://cgit.freedesktop.org/systemd/systemd/tree/src/libsystemd/sd-bus/bus-bloom.c + */ +static void +g_kdbus_setup_bloom (GKDBusWorker *worker, + GDBusMessage *dbus_msg, + struct kdbus_bloom_filter *bloom_filter) +{ + GVariant *body; + + const gchar *message_type; + const gchar *interface; + const gchar *member; + const gchar *path; + + void *bloom_data; + + body = g_dbus_message_get_body (dbus_msg); + message_type = _g_dbus_enum_to_string (G_TYPE_DBUS_MESSAGE_TYPE, g_dbus_message_get_message_type (dbus_msg)); + interface = g_dbus_message_get_interface (dbus_msg); + member = g_dbus_message_get_member (dbus_msg); + path = g_dbus_message_get_path (dbus_msg); + + bloom_data = bloom_filter->data; + memset (bloom_data, 0, worker->bloom_size); + bloom_filter->generation = 0; + + g_kdbus_bloom_add_pair(worker, bloom_data, "message-type", message_type); + + if (interface) + g_kdbus_bloom_add_pair(worker, bloom_data, "interface", interface); + + if (member) + g_kdbus_bloom_add_pair(worker, bloom_data, "member", member); + + if (path) + { + g_kdbus_bloom_add_pair(worker, bloom_data, "path", path); + g_kdbus_bloom_add_pair(worker, bloom_data, "path-slash-prefix", path); + g_kdbus_bloom_add_prefixes(worker, bloom_data, "path-slash-prefix", path, '/'); + } + + if (body != NULL) + { + const GVariantType *body_type; + const GVariantType *arg_type; + guint cnt; + + body_type = g_variant_get_type (body); + + for (arg_type = g_variant_type_first (body_type), cnt = 0; + arg_type; + arg_type = g_variant_type_next (arg_type), cnt++) + { + gchar type_char = g_variant_type_peek_string (arg_type)[0]; + gchar buf[sizeof("arg")-1 + 2 + sizeof("-slash-prefix")]; + const gchar *str; + GVariant *child; + gchar *e; + + if (type_char != 's' && type_char != 'o') + /* XXX: kdbus docs say "stop after first non-string" but I + * think they're wrong (vs. dbus-1 compat)... + */ + continue; + + child = g_variant_get_child_value (body, cnt); + str = g_variant_get_string (child, NULL); + + e = stpcpy(buf, "arg"); + if (cnt < 10) + *(e++) = '0' + (char) cnt; + else + { + *(e++) = '0' + (char) (cnt / 10); + *(e++) = '0' + (char) (cnt % 10); + } + + /* We add this one for both strings and object paths */ + strcpy(e, "-slash-prefix"); + g_kdbus_bloom_add_prefixes(worker, bloom_data, buf, str, '/'); + + /* But the others are only for strings */ + if (type_char == 's') + { + strcpy(e, "-dot-prefix"); + g_kdbus_bloom_add_prefixes(worker, bloom_data, buf, str, '.'); + + *e = 0; + g_kdbus_bloom_add_pair(worker, bloom_data, buf, str); + } + + g_variant_unref (child); + } + } +} + + +/* + * TODO: g_kdbus_NameOwnerChanged_generate, g_kdbus_KernelMethodError_generate + */ + +/** + * g_kdbus_decode_kernel_msg: + * + */ +static void +g_kdbus_decode_kernel_msg (GKDBusWorker *worker, + struct kdbus_msg *msg) { struct kdbus_item *item = NULL; - gssize size = 0; - KDBUS_ITEM_FOREACH(item, kdbus->priv->kmsg, items) + KDBUS_ITEM_FOREACH(item, msg, items) { switch (item->type) { @@ -1433,13 +1770,13 @@ g_kdbus_decode_kernel_msg (GKdbus *kdbus) case KDBUS_ITEM_NAME_ADD: case KDBUS_ITEM_NAME_REMOVE: case KDBUS_ITEM_NAME_CHANGE: - //size = g_kdbus_NameOwnerChanged_generate (kdbus, item); + //size = g_kdbus_NameOwnerChanged_generate (worker, item); g_error ("'NameOwnerChanged'"); break; case KDBUS_ITEM_REPLY_TIMEOUT: case KDBUS_ITEM_REPLY_DEAD: - //size = g_kdbus_KernelMethodError_generate (kdbus, item); + //size = g_kdbus_KernelMethodError_generate (worker, item); g_error ("'KernelMethodError'"); break; @@ -1450,19 +1787,227 @@ g_kdbus_decode_kernel_msg (GKdbus *kdbus) #if 0 /* Override information from the user header with data from the kernel */ - g_string_printf (kdbus->priv->msg_sender, "org.freedesktop.DBus"); + g_string_printf (worker->msg_sender, "org.freedesktop.DBus"); /* for destination */ - if (kdbus->priv->kmsg->dst_id == KDBUS_DST_ID_BROADCAST) + if (worker->kmsg->dst_id == KDBUS_DST_ID_BROADCAST) /* for broadcast messages we don't have to set destination */ ; - else if (kdbus->priv->kmsg->dst_id == KDBUS_DST_ID_NAME) - g_string_printf (kdbus->priv->msg_destination, ":1.%" G_GUINT64_FORMAT, (guint64) kdbus->priv->unique_id); + else if (worker->kmsg->dst_id == KDBUS_DST_ID_NAME) + g_string_printf (worker->msg_destination, ":1.%" G_GUINT64_FORMAT, (guint64) worker->unique_id); else - g_string_printf (kdbus->priv->msg_destination, ":1.%" G_GUINT64_FORMAT, (guint64) kdbus->priv->kmsg->dst_id); -#endif + g_string_printf (worker->msg_destination, ":1.%" G_GUINT64_FORMAT, (guint64) worker->kmsg->dst_id); return size; +#endif +} + + +/** + * g_kdbus_decode_dbus_msg: + * + */ +static GDBusMessage * +g_kdbus_decode_dbus_msg (GKDBusWorker *worker, + struct kdbus_msg *msg) +{ + GDBusMessage *message; + struct kdbus_item *item; + gssize data_size = 0; + GArray *body_vectors; + gsize body_size; + GVariant *body; + gchar *tmp; + guint i; + GVariant *parts[2]; + GVariantIter *fields_iter; + guint8 endianness, type, flags, version; + guint64 key; + GVariant *value; + guint64 serial; + + + message = g_dbus_message_new (); + + body_vectors = g_array_new (FALSE, FALSE, sizeof (GVariantVector)); + + tmp = g_strdup_printf (":1.%"G_GUINT64_FORMAT, (guint64) msg->src_id); + g_dbus_message_set_sender (message, tmp); + g_free (tmp); + + item = msg->items; + body_size = 0; + + KDBUS_ITEM_FOREACH(item, msg, items) + { + if (item->size <= KDBUS_ITEM_HEADER_SIZE) + g_error("[KDBUS] %llu bytes - invalid data record\n", item->size); + + data_size = item->size - KDBUS_ITEM_HEADER_SIZE; + + switch (item->type) + { + /* KDBUS_ITEM_DST_NAME */ + case KDBUS_ITEM_DST_NAME: + /* Classic D-Bus doesn't make this known to the receiver, so + * we don't do it here either (for compatibility with the + * fallback case). + */ + break; + + /* KDBUS_ITEM_PALOAD_OFF */ + case KDBUS_ITEM_PAYLOAD_OFF: + { + GVariantVector vector; + gsize flavour; + + /* We want to make sure the bytes are aligned the same as + * they would be if they appeared in a contiguously + * allocated chunk of aligned memory. + * + * We decide what the alignment 'should' be by consulting + * body_size, which has been tracking the total size of the + * message up to this point. + * + * We then play around with the pointer by removing as many + * bytes as required to get it to the proper alignment (and + * copy extra bytes accordingly). This means that we will + * grab some extra data in the 'bytes', but it won't be + * shared with GVariant (which means there is no chance of + * it being accidentally retransmitted). + * + * The kernel does the same thing, so make sure we get the + * expected result. Because of the kernel doing the same, + * the result is that we will always be rounding-down to a + * multiple of 8 for the pointer, which means that the + * pointer will always be valid, assuming the original + * address was. + * + * We could fix this with a new GBytes constructor that took + * 'flavour' as a parameter, but it's not worth it... + */ + flavour = body_size & 7; + g_assert ((item->vec.offset & 7) == flavour); + + vector.gbytes = g_bytes_new (((guchar *) msg) + item->vec.offset - flavour, item->vec.size + flavour); + vector.data.pointer = g_bytes_get_data (vector.gbytes, NULL); + vector.data.pointer += flavour; + vector.size = item->vec.size; + + g_array_append_val (body_vectors, vector); + body_size += vector.size; + } + break; + + /* KDBUS_ITEM_PAYLOAD_MEMFD */ + case KDBUS_ITEM_PAYLOAD_MEMFD: + { + GVariantVector vector; + const guchar *data; + gsize size; + + vector.gbytes = g_bytes_new_take_zero_copy_fd (item->memfd.fd); + data = g_bytes_get_data (vector.gbytes, &size); + + g_assert (item->memfd.start + item->memfd.size <= size); + + vector.data.pointer = data + item->memfd.start; + vector.size = item->memfd.size; + + g_array_append_val (body_vectors, vector); + body_size += vector.size; + } + break; + + /* KDBUS_ITEM_FDS */ + case KDBUS_ITEM_FDS: + { + GUnixFDList *fd_list; + + fd_list = g_unix_fd_list_new_from_array (item->fds, data_size / sizeof (int)); + g_dbus_message_set_unix_fd_list (message, fd_list); + g_object_unref (fd_list); + } + break; + + /* All of the following items, like CMDLINE, + CGROUP, etc. need some GDBUS API extensions and + should be implemented in the future */ + case KDBUS_ITEM_TIMESTAMP: + { + g_print ("time: seq %llu mon %llu real %llu\n", + item->timestamp.seqnum, item->timestamp.monotonic_ns, item->timestamp.realtime_ns); + //g_dbus_message_set_timestamp (message, item->timestamp.monotonic_ns / 1000); + //g_dbus_message_set_serial (message, item->timestamp.seqnum); + break; + } + + case KDBUS_ITEM_CREDS: + { + g_print ("creds: u%u eu %u su%u fsu%u g%u eg%u sg%u fsg%u\n", + item->creds.uid, item->creds.euid, item->creds.suid, item->creds.fsuid, + item->creds.gid, item->creds.egid, item->creds.sgid, item->creds.fsgid); + break; + } + + case KDBUS_ITEM_PIDS: + case KDBUS_ITEM_PID_COMM: + case KDBUS_ITEM_TID_COMM: + case KDBUS_ITEM_EXE: + case KDBUS_ITEM_CMDLINE: + case KDBUS_ITEM_CGROUP: + case KDBUS_ITEM_AUDIT: + case KDBUS_ITEM_CAPS: + case KDBUS_ITEM_SECLABEL: + case KDBUS_ITEM_CONN_DESCRIPTION: + case KDBUS_ITEM_AUXGROUPS: + case KDBUS_ITEM_OWNED_NAME: + case KDBUS_ITEM_NAME: + g_print ("unhandled %04x\n", (int) item->type); + break; + + default: + g_error ("[KDBUS] DBUS_PAYLOAD: Unknown filed - %lld", item->type); + break; + } + } + + body = GLIB_PRIVATE_CALL(g_variant_from_vectors) (G_VARIANT_TYPE ("((yyyyuta{tv})v)"), + (GVariantVector *) body_vectors->data, + body_vectors->len, body_size, FALSE); + g_assert (body); + + for (i = 0; i < body_vectors->len; i++) + g_bytes_unref (g_array_index (body_vectors, GVariantVector, i).gbytes); + + g_array_free (body_vectors, TRUE); + + parts[0] = g_variant_get_child_value (body, 0); + parts[1] = g_variant_get_child_value (body, 1); + g_variant_unref (body); + + body = g_variant_get_variant (parts[1]); + g_variant_unref (parts[1]); + g_dbus_message_set_body (message, body); + g_variant_unref (body); + + g_variant_get (parts[0], "(yyyyuta{tv})", &endianness, &type, &flags, &version, NULL, &serial, &fields_iter); + g_variant_unref (parts[0]); + + while (g_variant_iter_loop (fields_iter, "{tv}", &key, &value)) + g_dbus_message_set_header (message, key, value); + + g_dbus_message_set_flags (message, flags); + g_dbus_message_set_serial (message, serial); + g_dbus_message_set_message_type (message, type); + + g_print ("Received:\n%s\n", g_dbus_message_print (message, 2)); + + (* worker->message_received_callback) (message, worker->user_data); + + g_object_unref (message); + + return 0; } @@ -1470,23 +2015,33 @@ g_kdbus_decode_kernel_msg (GKdbus *kdbus) * _g_kdbus_receive: * */ -gssize -_g_kdbus_receive (GKdbus *kdbus, +static gssize +_g_kdbus_receive (GKDBusWorker *kdbus, GCancellable *cancellable, GError **error) { - struct kdbus_cmd_recv recv = {}; - gssize size = 0; + struct kdbus_cmd_recv recv; + struct kdbus_msg *msg; + + memset (&recv, 0, sizeof recv); + recv.size = sizeof (recv); if (g_cancellable_set_error_if_cancelled (cancellable, error)) return -1; again: - if (ioctl(kdbus->priv->fd, KDBUS_CMD_MSG_RECV, &recv) < 0) + if (ioctl(kdbus->fd, KDBUS_CMD_RECV, &recv) < 0) { - if (errno == EINTR || errno == EAGAIN) + if (errno == EINTR) goto again; + if (errno == EAGAIN) + return 0; + + g_warning ("in holding pattern over %d %d\n", kdbus->fd, errno); + while (1) + sleep(1); + g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errno), _("Error while receiving message: %s"), @@ -1494,12 +2049,14 @@ again: return -1; } - kdbus->priv->kmsg = (struct kdbus_msg *)((guint8 *)kdbus->priv->kdbus_buffer + recv.offset); + g_print ("but sometimes that's okay\n"); - if (kdbus->priv->kmsg->payload_type == KDBUS_PAYLOAD_DBUS) - g_error ("Received standard dbus message - not supported yet"); - else if (kdbus->priv->kmsg->payload_type == KDBUS_PAYLOAD_KERNEL) - size = g_kdbus_decode_kernel_msg (kdbus); + msg = (struct kdbus_msg *)((guint8 *)kdbus->kdbus_buffer + recv.msg.offset); + + if (msg->payload_type == KDBUS_PAYLOAD_DBUS) + g_kdbus_decode_dbus_msg (kdbus, msg); + else if (msg->payload_type == KDBUS_PAYLOAD_KERNEL) + g_kdbus_decode_kernel_msg (kdbus, msg); else { g_set_error (error, @@ -1509,5 +2066,527 @@ again: return -1; } - return size; + ioctl(kdbus->fd, KDBUS_CMD_FREE, &recv.msg.offset); + + return 0; +} + +static gboolean +g_kdbus_msg_append_item (struct kdbus_msg *msg, + gsize type, + gconstpointer data, + gsize size) +{ + struct kdbus_item *item; + gsize item_size; + + item_size = size + G_STRUCT_OFFSET(struct kdbus_item, data); + + if (msg->size + item_size > KDBUS_MSG_MAX_SIZE) + return FALSE; + + /* align */ + msg->size += (-msg->size) & 7; + item = (struct kdbus_item *) ((guchar *) msg + msg->size); + item->type = type; + item->size = item_size; + memcpy (item->data, data, size); + + msg->size += item_size; + + return TRUE; +} + +static gboolean +g_kdbus_msg_append_payload_vec (struct kdbus_msg *msg, + gconstpointer data, + gsize size) +{ + struct kdbus_vec vec = { + .size = size, + .address = (gsize) data + }; + + return g_kdbus_msg_append_item (msg, KDBUS_ITEM_PAYLOAD_VEC, &vec, sizeof vec); +} + +static gboolean +g_kdbus_msg_append_payload_memfd (struct kdbus_msg *msg, + gint fd, + gsize offset, + gsize size) +{ + struct kdbus_memfd mfd = { + .start = offset, + .size = size, + .fd = fd, + }; + + return g_kdbus_msg_append_item (msg, KDBUS_ITEM_PAYLOAD_MEMFD, &mfd, sizeof mfd); +} + +#if 0 +#include "dbusheader.h" + +void dump_header (gconstpointer data, gsize size) ; +void +dump_header (gconstpointer data, + gsize size) +{ + GDBusMessageHeaderFieldsIterator iter; + GDBusMessageHeader header; + + header = g_dbus_message_header_new (data, size); + g_print ("header e/%c t/%u f/x%x v/%u s/%"G_GUINT64_FORMAT"\n", + g_dbus_message_header_get_endian (header), + g_dbus_message_header_get_type (header), + g_dbus_message_header_get_flags (header), + g_dbus_message_header_get_version (header), + g_dbus_message_header_get_serial (header)); + + iter = g_dbus_message_header_iterate_fields (header); + + while (g_dbus_message_header_fields_iterator_next (&iter)) + { + const gchar *string; + guint64 reply_to; + guint64 key; + + key = g_dbus_message_header_fields_iterator_get_key (iter); + + switch (key) + { + case G_DBUS_MESSAGE_HEADER_FIELD_PATH: + if (g_dbus_message_header_fields_iterator_get_value_as_object_path (iter, &string)) + g_print (" path: %s\n", string); + else + g_print (" path: <>\n"); + break; + + case G_DBUS_MESSAGE_HEADER_FIELD_INTERFACE: + if (g_dbus_message_header_fields_iterator_get_value_as_string (iter, &string)) + g_print (" interface: %s\n", string); + else + g_print (" interface: <>\n"); + break; + + case G_DBUS_MESSAGE_HEADER_FIELD_MEMBER: + if (g_dbus_message_header_fields_iterator_get_value_as_string (iter, &string)) + g_print (" member: %s\n", string); + else + g_print (" member: <>\n"); + break; + + case G_DBUS_MESSAGE_HEADER_FIELD_ERROR_NAME: + if (g_dbus_message_header_fields_iterator_get_value_as_string (iter, &string)) + g_print (" error: %s\n", string); + else + g_print (" error: <>\n"); + break; + + case G_DBUS_MESSAGE_HEADER_FIELD_REPLY_SERIAL: + if (g_dbus_message_header_fields_iterator_get_value_as_string (iter, &string)) + g_print (" serial: %s\n", string); + else + g_print (" serial: <>\n"); + break; + + case G_DBUS_MESSAGE_HEADER_FIELD_DESTINATION: + if (g_dbus_message_header_fields_iterator_get_value_as_string (iter, &string)) + g_print (" destination: %s\n", string); + else + g_print (" destination: <>\n"); + break; + + case G_DBUS_MESSAGE_HEADER_FIELD_SENDER: + if (g_dbus_message_header_fields_iterator_get_value_as_string (iter, &string)) + g_print (" sender: %s\n", string); + else + g_print (" sender: <>\n"); + break; + + default: + g_print ("unknown field code %"G_GUINT64_FORMAT"\n", key); + g_assert_not_reached (); + } + } + + g_print ("\n"); + +} +#endif + +/** + * _g_kdbus_send: + * Returns: size of data sent or -1 when error + */ +static gboolean +_g_kdbus_send (GKDBusWorker *kdbus, + GDBusMessage *message, + GError **error) +{ + struct kdbus_msg *msg = alloca (KDBUS_MSG_MAX_SIZE); + GVariantVectors body_vectors; + struct kdbus_cmd_send send; + + g_return_val_if_fail (G_IS_KDBUS_WORKER (kdbus), -1); + + /* fill in as we go... */ + memset (msg, 0, sizeof (struct kdbus_msg)); + msg->size = sizeof (struct kdbus_msg); + msg->payload_type = KDBUS_PAYLOAD_DBUS; + msg->src_id = kdbus->unique_id; + msg->cookie = g_dbus_message_get_serial(message); + + /* Message destination */ + { + const gchar *dst_name; + + dst_name = g_dbus_message_get_destination (message); + + if (dst_name != NULL) + { + if (g_dbus_is_unique_name (dst_name)) + { + if (dst_name[1] != '1' || dst_name[2] != '.') + { + g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_NAME_HAS_NO_OWNER, + "Invalid unique D-Bus name '%s'", dst_name); + return FALSE; + } + + /* We already know that it passes the checks for unique + * names, so no need to perform error checking on strtoull. + */ + msg->dst_id = strtoull (dst_name + 3, NULL, 10); + dst_name = NULL; + } + else + { + g_kdbus_msg_append_item (msg, KDBUS_ITEM_DST_NAME, dst_name, strlen (dst_name) + 1); + msg->dst_id = KDBUS_DST_ID_NAME; + } + } + else + msg->dst_id = KDBUS_DST_ID_BROADCAST; + } + + /* File descriptors */ + { + GUnixFDList *fd_list; + + fd_list = g_dbus_message_get_unix_fd_list (message); + + if (fd_list != NULL) + { + const gint *fds; + gint n_fds; + + fds = g_unix_fd_list_peek_fds (fd_list, &n_fds); + + if (n_fds) + g_kdbus_msg_append_item (msg, KDBUS_ITEM_FDS, fds, sizeof (gint) * n_fds); + } + } + + /* Message body */ + { + struct dbus_fixed_header fh; + GHashTableIter header_iter; + GVariantBuilder builder; + gpointer key, value; + GVariant *parts[3]; + GVariant *body; + + fh.endian = (G_BYTE_ORDER == G_LITTLE_ENDIAN) ? 'l': 'B'; + fh.type = g_dbus_message_get_message_type (message); + fh.flags = g_dbus_message_get_flags (message); + fh.version = 2; + fh.reserved = 0; + fh.serial = g_dbus_message_get_serial (message); + parts[0] = g_variant_new_from_data (DBUS_FIXED_HEADER_TYPE, &fh, sizeof fh, TRUE, NULL, NULL); + + g_dbus_message_init_header_iter (message, &header_iter); + g_variant_builder_init (&builder, DBUS_EXTENDED_HEADER_TYPE); + + /* We set the sender field to the correct value for ourselves */ + g_variant_builder_add (&builder, "{tv}", + (guint64) G_DBUS_MESSAGE_HEADER_FIELD_SENDER, + g_variant_new_printf (":1.%"G_GUINT64_FORMAT, kdbus->unique_id)); + + while (g_hash_table_iter_next (&header_iter, &key, &value)) + { + guint64 key_int = (gsize) key; + + switch (key_int) + { + /* These are the normal header fields that get passed + * straight through. + */ + case G_DBUS_MESSAGE_HEADER_FIELD_PATH: + case G_DBUS_MESSAGE_HEADER_FIELD_INTERFACE: + case G_DBUS_MESSAGE_HEADER_FIELD_MEMBER: + case G_DBUS_MESSAGE_HEADER_FIELD_ERROR_NAME: + case G_DBUS_MESSAGE_HEADER_FIELD_REPLY_SERIAL: + case G_DBUS_MESSAGE_HEADER_FIELD_DESTINATION: + g_variant_builder_add (&builder, "{tv}", key_int, value); + /* This is a little bit gross. + * + * We must send the header part of the message in a single + * vector as per kdbus rules, but the GVariant serialiser + * code will split any item >= 128 bytes into its own + * vector to save the copy. + * + * No header field should be that big anyway... right? + */ + g_assert_cmpint (g_variant_get_size (value), <, 128); + continue; + + /* We send this one unconditionally, but set it ourselves */ + case G_DBUS_MESSAGE_HEADER_FIELD_SENDER: + continue; + + /* We don't send these at all in GVariant format */ + case G_DBUS_MESSAGE_HEADER_FIELD_SIGNATURE: + case G_DBUS_MESSAGE_HEADER_FIELD_NUM_UNIX_FDS: + continue; + + default: + g_assert_not_reached (); + } + } + parts[1] = g_variant_builder_end (&builder); + + body = g_dbus_message_get_body (message); + if (!body) + body = g_variant_new ("()"); + parts[2] = g_variant_new_variant (body); + + body = g_variant_ref_sink (g_variant_new_tuple (parts, G_N_ELEMENTS (parts))); + GLIB_PRIVATE_CALL(g_variant_to_vectors) (body, &body_vectors); + + /* Sanity check to make sure the header is really contiguous: + * + * - we must have at least one vector in the output + * - the first vector must completely contain at least the header + */ + g_assert_cmpint (body_vectors.vectors->len, >, 0); + g_assert_cmpint (g_array_index (body_vectors.vectors, GVariantVector, 0).size, >=, + g_variant_get_size (parts[0]) + g_variant_get_size (parts[1])); + + g_variant_unref (body); + } + + { + guint i; + + for (i = 0; i < body_vectors.vectors->len; i++) + { + GVariantVector vector = g_array_index (body_vectors.vectors, GVariantVector, i); + + if (vector.gbytes) + { + gint fd; + + fd = g_bytes_get_zero_copy_fd (vector.gbytes); + + if (fd >= 0) + { + const guchar *bytes_data; + gsize bytes_size; + + bytes_data = g_bytes_get_data (vector.gbytes, &bytes_size); + + if (bytes_data <= vector.data.pointer && vector.data.pointer + vector.size <= bytes_data + bytes_size) + { + if (!g_kdbus_msg_append_payload_memfd (msg, fd, vector.data.pointer - bytes_data, vector.size)) + goto need_compact; + } + else + { + if (!g_kdbus_msg_append_payload_vec (msg, vector.data.pointer, vector.size)) + goto need_compact; + } + } + else + { + if (!g_kdbus_msg_append_payload_vec (msg, vector.data.pointer, vector.size)) + goto need_compact; + } + } + else + if (!g_kdbus_msg_append_payload_vec (msg, body_vectors.extra_bytes->data + vector.data.offset, vector.size)) + goto need_compact; + } + } + + /* + * set message flags + */ + msg->flags = ((g_dbus_message_get_flags (message) & G_DBUS_MESSAGE_FLAGS_NO_REPLY_EXPECTED) ? 0 : KDBUS_MSG_EXPECT_REPLY) | + ((g_dbus_message_get_flags (message) & G_DBUS_MESSAGE_FLAGS_NO_AUTO_START) ? KDBUS_MSG_NO_AUTO_START : 0); + + if ((msg->flags) & KDBUS_MSG_EXPECT_REPLY) + msg->timeout_ns = 1000LU * g_get_monotonic_time() + KDBUS_TIMEOUT_NS; + else + msg->cookie_reply = g_dbus_message_get_reply_serial(message); + + + /* + if (dst_id == KDBUS_DST_ID_BROADCAST) + { + struct kdbus_bloom_filter *bloom_filter; + + bloom_filter = g_kdbus_append_bloom (&item, kdbus->bloom_size); + g_kdbus_setup_bloom (kdbus, message, bloom_filter); + } + */ + + send.size = sizeof (send); + send.flags = 0; + send.msg_address = (gsize) msg; + + /* + * send message + */ +//again: + if (ioctl(kdbus->fd, KDBUS_CMD_SEND, &send)) + { +/* + GString *error_name; + error_name = g_string_new (NULL); + + if(errno == EINTR) + { + g_string_free (error_name,TRUE); + goto again; + } + else if (errno == ENXIO) + { + g_string_printf (error_name, "Name %s does not exist", g_dbus_message_get_destination(dbus_msg)); + g_kdbus_generate_local_error (worker, + dbus_msg, + g_variant_new ("(s)",error_name->str), + G_DBUS_ERROR_SERVICE_UNKNOWN); + g_string_free (error_name,TRUE); + return 0; + } + else if ((errno == ESRCH) || (errno == EADDRNOTAVAIL)) + { + if (kmsg->flags & KDBUS_MSG_FLAGS_NO_AUTO_START) + { + g_string_printf (error_name, "Name %s does not exist", g_dbus_message_get_destination(dbus_msg)); + g_kdbus_generate_local_error (worker, + dbus_msg, + g_variant_new ("(s)",error_name->str), + G_DBUS_ERROR_SERVICE_UNKNOWN); + g_string_free (error_name,TRUE); + return 0; + } + else + { + g_string_printf (error_name, "The name %s was not provided by any .service files", g_dbus_message_get_destination(dbus_msg)); + g_kdbus_generate_local_error (worker, + dbus_msg, + g_variant_new ("(s)",error_name->str), + G_DBUS_ERROR_SERVICE_UNKNOWN); + g_string_free (error_name,TRUE); + return 0; + } + } + + g_print ("[KDBUS] ioctl error sending kdbus message:%d (%m)\n",errno); + g_set_error (error, G_IO_ERROR, g_io_error_from_errno(errno), _("Error sending message - KDBUS_CMD_MSG_SEND error")); +*/ + perror("ioctl send"); + g_error ("IOCTL SEND: %d\n",errno); + return FALSE; + } + + return TRUE; + +need_compact: + /* We end up here if: + * - too many kdbus_items + * - too large kdbus_msg size + * - too much vector data + * + * We need to compact the message before sending it. + */ + g_assert_not_reached (); +} + +GKDBusWorker * +g_kdbus_worker_new (const gchar *address, + GError **error) +{ + GKDBusWorker *worker; + + worker = g_object_new (G_TYPE_KDBUS_WORKER, NULL); + if (!_g_kdbus_open (worker, address, error)) + { + g_object_unref (worker); + return NULL; + } + + return worker; +} + +void +g_kdbus_worker_associate (GKDBusWorker *worker, + GDBusCapabilityFlags capabilities, + GDBusWorkerMessageReceivedCallback message_received_callback, + GDBusWorkerMessageAboutToBeSentCallback message_about_to_be_sent_callback, + GDBusWorkerDisconnectedCallback disconnected_callback, + gpointer user_data) +{ + worker->capabilities = capabilities; + worker->message_received_callback = message_received_callback; + worker->message_about_to_be_sent_callback = message_about_to_be_sent_callback; + worker->disconnected_callback = disconnected_callback; + worker->user_data = user_data; +} + +void +g_kdbus_worker_unfreeze (GKDBusWorker *worker) +{ + gchar *name; + + if (worker->source != NULL) + return; + + worker->context = g_main_context_ref_thread_default (); + worker->source = g_unix_fd_source_new (worker->fd, G_IO_IN); + + g_source_set_callback (worker->source, (GSourceFunc) kdbus_ready, worker, NULL); + name = g_strdup_printf ("kdbus worker"); + g_source_set_name (worker->source, name); + g_free (name); + + g_source_attach (worker->source, worker->context); +} + +gboolean +g_kdbus_worker_send_message (GKDBusWorker *worker, + GDBusMessage *message, + GError **error) +{ + return _g_kdbus_send (worker, message, error); +} + +void +g_kdbus_worker_stop (GKDBusWorker *worker) +{ +} + +void +g_kdbus_worker_flush_sync (GKDBusWorker *worker) +{ +} + +void +g_kdbus_worker_close (GKDBusWorker *worker, + GCancellable *cancellable, + GSimpleAsyncResult *result) +{ }