summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAdrian Szyndela <adrian.s@samsung.com>2016-01-07 14:48:48 +0100
committerAdrian Szyndela <adrian.s@samsung.com>2016-01-13 15:11:46 +0100
commit59067c7f66bfdb199da221fde56ad59b09a3fd27 (patch)
treefdb10cec44955d3c9c360f632ba5a4e34b89844b
parentebc0fa80b2903379a9e179915fbb10fc7ecd7575 (diff)
downloaddbus-59067c7f66bfdb199da221fde56ad59b09a3fd27.tar.gz
dbus-59067c7f66bfdb199da221fde56ad59b09a3fd27.tar.bz2
dbus-59067c7f66bfdb199da221fde56ad59b09a3fd27.zip
This reverts commit 197db0df78e0a9f82df4162885854f99ff749505, which reverts commit 771f5155c9a393a242329988d56661a51fc04e6c. Change-Id: I4133020c585a6f9b42f487ffae4d2005b25a1d08
-rw-r--r--bus/main.c2
-rw-r--r--configure.ac25
-rw-r--r--dbus/Makefile.am22
-rw-r--r--dbus/dbus-asv-util.c54
-rw-r--r--dbus/dbus-asv-util.h30
-rw-r--r--dbus/dbus-auth.c33
-rw-r--r--dbus/dbus-auth.h1
-rw-r--r--dbus/dbus-connection-internal.h1
-rw-r--r--dbus/dbus-connection.c116
-rw-r--r--dbus/dbus-marshal-gvariant.c1388
-rw-r--r--dbus/dbus-marshal-gvariant.h132
-rw-r--r--dbus/dbus-marshal-header.c304
-rw-r--r--dbus/dbus-marshal-header.h4
-rw-r--r--dbus/dbus-marshal-recursive.c496
-rw-r--r--dbus/dbus-marshal-recursive.h29
-rw-r--r--dbus/dbus-message-internal.h12
-rw-r--r--dbus/dbus-message-private.h3
-rw-r--r--dbus/dbus-message.c620
-rw-r--r--dbus/dbus-message.h85
-rw-r--r--dbus/dbus-pending-call.c3
-rw-r--r--dbus/dbus-protocol-gvariant.h56
-rw-r--r--dbus/dbus-shared.h2
-rw-r--r--dbus/dbus-signals.c1633
-rw-r--r--dbus/dbus-signals.h117
-rw-r--r--dbus/dbus-transport-kdbus.c4000
-rw-r--r--dbus/dbus-transport-kdbus.h34
-rw-r--r--dbus/dbus-transport-protected.h19
-rw-r--r--dbus/dbus-transport.c221
-rw-r--r--dbus/dbus-transport.h4
-rw-r--r--dbus/kdbus-common.c1026
-rw-r--r--dbus/kdbus-common.h197
-rw-r--r--dbus/kdbus.h984
-rw-r--r--packaging/dbus-x11.spec4
-rw-r--r--packaging/dbus.spec5
-rw-r--r--packaging/libdbus.spec5
35 files changed, 11206 insertions, 461 deletions
diff --git a/bus/main.c b/bus/main.c
index e060baa8..702b5194 100644
--- a/bus/main.c
+++ b/bus/main.c
@@ -643,6 +643,8 @@ main (int argc, char **argv)
_dbus_set_signal_handler (SIGHUP, signal_handler);
#endif /* DBUS_UNIX */
+ dbus_set_protocol_version (DBUS_MAJOR_PROTOCOL_VERSION);
+
_dbus_verbose ("We are on D-Bus...\n");
_dbus_loop_run (bus_context_get_loop (context));
diff --git a/configure.ac b/configure.ac
index ca2c47cf..e381860e 100644
--- a/configure.ac
+++ b/configure.ac
@@ -158,6 +158,7 @@ AC_ARG_ENABLE(kqueue, AS_HELP_STRING([--enable-kqueue],[build with kqueue suppor
AC_ARG_ENABLE(console-owner-file, AS_HELP_STRING([--enable-console-owner-file],[enable console owner file]),enable_console_owner_file=$enableval,enable_console_owner_file=auto)
AC_ARG_ENABLE(launchd, AS_HELP_STRING([--enable-launchd],[build with launchd auto-launch support]),enable_launchd=$enableval,enable_launchd=auto)
AC_ARG_ENABLE(systemd, AS_HELP_STRING([--enable-systemd],[build with systemd at_console support]),enable_systemd=$enableval,enable_systemd=auto)
+AC_ARG_ENABLE(kdbus-transport, AS_HELP_STRING([--enable-kdbus-transport],[build with kdbus transport support]),enable_kdbus_transport=$enableval,enable_kdbus_transport=no)
AC_ARG_WITH(init-scripts, AS_HELP_STRING([--with-init-scripts=[redhat]],[Style of init scripts to install]))
AC_ARG_WITH(session-socket-dir, AS_HELP_STRING([--with-session-socket-dir=[dirname]],[Where to put sockets for the per-login-session message bus]))
@@ -1238,6 +1239,12 @@ if test x$with_valgrind != xno; then
AC_DEFINE([WITH_VALGRIND], [1], [Define to add Valgrind instrumentation])
fi
+### kdbus support
+if test x$enable_kdbus_transport = xyes; then
+ AC_DEFINE(ENABLE_KDBUS_TRANSPORT,1,[Enable kdbus transport support])
+fi
+AM_CONDITIONAL([ENABLE_KDBUS_TRANSPORT], [test x$enable_kdbus_transport = xyes])
+
# Determine maximum number of Unix fds which may be passed
AS_CASE([$host_os],
[*qnx*],
@@ -1250,7 +1257,7 @@ AC_DEFINE_UNQUOTED([DBUS_DEFAULT_MESSAGE_UNIX_FDS],
AC_SUBST([DEFAULT_MESSAGE_UNIX_FDS])
#### Set up final flags
-LIBDBUS_LIBS="$THREAD_LIBS $NETWORK_libs"
+LIBDBUS_LIBS="$THREAD_LIBS $NETWORK_libs $KDBUS_LIBS"
AC_SUBST([LIBDBUS_LIBS])
### X11 detection
@@ -1526,7 +1533,20 @@ AC_DEFINE_UNQUOTED(DBUS_SYSTEM_SOCKET,"$DBUS_SYSTEM_SOCKET",[The name of the soc
## and also to connect to. If this ever changes, it'll need to be split into
## two variables, one for the listening address and one for the connecting
## address.
-DBUS_SYSTEM_BUS_DEFAULT_ADDRESS="unix:path=$DBUS_SYSTEM_SOCKET"
+#AC_SUBST(DBUS_SYSTEM_BUS_DEFAULT_ADDRESS)
+if ! test -z "$with_system_default_bus"; then
+ ## Now system bus can work on kdbus too. It is diffrent situation than
+ ## described above (daemon working with kdbus doesn't need to listen on
+ ## any socket), so variable was not splited into two.
+ DBUS_SYSTEM_BUS_DEFAULT_ADDRESS=$with_system_default_bus
+else
+ kdbus_address_path=""
+ if test x$enable_kdbus_transport = xyes; then
+ kdbus_address_path="kernel:path=/sys/fs/kdbus/0-system/bus;"
+ fi
+
+ DBUS_SYSTEM_BUS_DEFAULT_ADDRESS="${kdbus_address_path}unix:path=$DBUS_SYSTEM_SOCKET"
+fi
AC_SUBST(DBUS_SYSTEM_BUS_DEFAULT_ADDRESS)
AC_DEFINE_UNQUOTED(DBUS_SYSTEM_BUS_DEFAULT_ADDRESS, "$DBUS_SYSTEM_BUS_DEFAULT_ADDRESS",[The default D-Bus address of the system bus])
@@ -1865,6 +1885,7 @@ echo "
Building Doxygen docs: ${enable_doxygen_docs}
Building XML docs: ${enable_xml_docs}
Building launchd support: ${have_launchd}
+ Building kdbus support: ${enable_kdbus_transport}
Init scripts style: ${with_init_scripts}
Abstract socket names: ${ac_cv_have_abstract_sockets}
System bus socket: ${DBUS_SYSTEM_SOCKET}
diff --git a/dbus/Makefile.am b/dbus/Makefile.am
index 86d5d466..fa712428 100644
--- a/dbus/Makefile.am
+++ b/dbus/Makefile.am
@@ -112,6 +112,21 @@ DBUS_SHARED_arch_sources = \
sd-daemon.c \
sd-daemon.h
+DBUS_SHARED_arch_sources += \
+ dbus-marshal-gvariant.c \
+ dbus-marshal-gvariant.h \
+ dbus-protocol-gvariant.h
+
+if ENABLE_KDBUS_TRANSPORT
+DBUS_SHARED_arch_sources += \
+ dbus-transport-kdbus.c \
+ dbus-transport-kdbus.h \
+ kdbus-common.c \
+ kdbus-common.h \
+ dbus-signals.c \
+ dbus-signals.h
+endif
+
DBUS_UTIL_arch_sources = \
dbus-sysdeps-util-unix.c \
dbus-userdb-util.c \
@@ -230,7 +245,10 @@ DBUS_SHARED_SOURCES= \
$(DBUS_SHARED_arch_sources) \
dbus-sysdeps.c \
dbus-sysdeps.h \
- dbus-valgrind-internal.h
+ dbus-valgrind-internal.h \
+ dbus-asv-util.c \
+ dbus-asv-util.h \
+ dbus-string-util.c
### source code that is generic utility functionality used
### by the bus daemon or test apps, but is NOT included
@@ -238,8 +256,6 @@ DBUS_SHARED_SOURCES= \
### should be underscore-prefixed but don't really need
### to be unless they move to DBUS_SHARED_SOURCES later)
DBUS_UTIL_SOURCES= \
- dbus-asv-util.c \
- dbus-asv-util.h \
dbus-auth-script.c \
dbus-auth-script.h \
dbus-auth-util.c \
diff --git a/dbus/dbus-asv-util.c b/dbus/dbus-asv-util.c
index 583e41fa..e4440f4c 100644
--- a/dbus/dbus-asv-util.c
+++ b/dbus/dbus-asv-util.c
@@ -258,3 +258,57 @@ _dbus_asv_add_string (DBusMessageIter *arr_iter,
return TRUE;
}
+
+/**
+ * Create a new entry in an a{sv} (map from string to variant)
+ * with an array of bytes value.
+ *
+ * If this function fails, the a{sv} must be abandoned, for instance
+ * with _dbus_asv_abandon().
+ *
+ * @param arr_iter the iterator which is appending to the array
+ * @param key a UTF-8 key for the map
+ * @param value the value
+ * @returns #TRUE on success, or #FALSE if not enough memory
+ */
+dbus_bool_t
+_dbus_asv_add_array_of_bytes (DBusMessageIter *arr_iter,
+ const char *key,
+ const void *value,
+ unsigned int value_len)
+{
+ DBusMessageIter entry_iter, var_iter, sub_array_iter;
+
+ if (!_dbus_asv_open_entry (arr_iter, &entry_iter, key,
+ DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_BYTE_AS_STRING,
+ &var_iter))
+ return FALSE;
+
+ if (!dbus_message_iter_open_container (&var_iter,
+ DBUS_TYPE_ARRAY,
+ DBUS_TYPE_BYTE_AS_STRING,
+ &sub_array_iter))
+ {
+ _dbus_asv_abandon_entry (arr_iter, &entry_iter, &var_iter);
+ return FALSE;
+ }
+
+ if (!dbus_message_iter_append_fixed_array (&sub_array_iter, DBUS_TYPE_BYTE,
+ &value, value_len))
+ {
+ dbus_message_iter_abandon_container (&var_iter, &sub_array_iter);
+ _dbus_asv_abandon_entry (arr_iter, &entry_iter, &var_iter);
+ return FALSE;
+ }
+
+ if (!dbus_message_iter_close_container (&var_iter, &sub_array_iter))
+ {
+ _dbus_asv_abandon_entry (arr_iter, &entry_iter, &var_iter);
+ return FALSE;
+ }
+
+ if (!_dbus_asv_close_entry (arr_iter, &entry_iter, &var_iter))
+ return FALSE;
+
+ return TRUE;
+}
diff --git a/dbus/dbus-asv-util.h b/dbus/dbus-asv-util.h
index 0337260a..4c270bef 100644
--- a/dbus/dbus-asv-util.h
+++ b/dbus/dbus-asv-util.h
@@ -28,19 +28,23 @@
DBUS_BEGIN_DECLS
-DBusMessage *_dbus_asv_new_method_return (DBusMessage *message,
- DBusMessageIter *iter,
- DBusMessageIter *arr_iter);
-dbus_bool_t _dbus_asv_close (DBusMessageIter *iter,
- DBusMessageIter *arr_iter);
-void _dbus_asv_abandon (DBusMessageIter *iter,
- DBusMessageIter *arr_iter);
+DBusMessage *_dbus_asv_new_method_return (DBusMessage *message,
+ DBusMessageIter *iter,
+ DBusMessageIter *arr_iter);
+dbus_bool_t _dbus_asv_close (DBusMessageIter *iter,
+ DBusMessageIter *arr_iter);
+void _dbus_asv_abandon (DBusMessageIter *iter,
+ DBusMessageIter *arr_iter);
-dbus_bool_t _dbus_asv_add_uint32 (DBusMessageIter *arr_iter,
- const char *key,
- dbus_uint32_t value);
-dbus_bool_t _dbus_asv_add_string (DBusMessageIter *arr_iter,
- const char *key,
- const char *value);
+dbus_bool_t _dbus_asv_add_uint32 (DBusMessageIter *arr_iter,
+ const char *key,
+ dbus_uint32_t value);
+dbus_bool_t _dbus_asv_add_string (DBusMessageIter *arr_iter,
+ const char *key,
+ const char *value);
+dbus_bool_t _dbus_asv_add_array_of_bytes (DBusMessageIter *arr_iter,
+ const char *key,
+ const void *value,
+ unsigned int value_len);
#endif
diff --git a/dbus/dbus-auth.c b/dbus/dbus-auth.c
index 6a07665f..20af83d2 100644
--- a/dbus/dbus-auth.c
+++ b/dbus/dbus-auth.c
@@ -2322,6 +2322,39 @@ _dbus_auth_client_new (void)
}
/**
+ * Creates a new auth conversation object for the client side.
+ * In fact it only initialize structures and sets authenticated state
+ * and leaves authentication to different transport-dependent
+ * mechanisms.
+ *
+ * @returns the new object or #NULL if no memory
+ */
+DBusAuth*
+_dbus_auth_client_new_authenticated (void)
+{
+ DBusAuth *auth;
+ DBusString guid_str;
+
+ if (!_dbus_string_init (&guid_str))
+ return NULL;
+
+ auth = _dbus_auth_new (sizeof (DBusAuthClient));
+ if (auth == NULL)
+ {
+ _dbus_string_free (&guid_str);
+ return NULL;
+ }
+
+ DBUS_AUTH_CLIENT (auth)->guid_from_server = guid_str;
+
+ auth->side = auth_side_client;
+ auth->state = &common_state_authenticated;
+ auth->unix_fd_negotiated = TRUE;
+
+ return auth;
+}
+
+/**
* Increments the refcount of an auth object.
*
* @param auth the auth conversation
diff --git a/dbus/dbus-auth.h b/dbus/dbus-auth.h
index ba1975f7..9959b991 100644
--- a/dbus/dbus-auth.h
+++ b/dbus/dbus-auth.h
@@ -43,6 +43,7 @@ typedef enum
DBusAuth* _dbus_auth_server_new (const DBusString *guid);
DBusAuth* _dbus_auth_client_new (void);
+DBusAuth* _dbus_auth_client_new_authenticated (void);
DBusAuth* _dbus_auth_ref (DBusAuth *auth);
void _dbus_auth_unref (DBusAuth *auth);
dbus_bool_t _dbus_auth_set_mechanisms (DBusAuth *auth,
diff --git a/dbus/dbus-connection-internal.h b/dbus/dbus-connection-internal.h
index 682b5bff..3553f7db 100644
--- a/dbus/dbus-connection-internal.h
+++ b/dbus/dbus-connection-internal.h
@@ -110,6 +110,7 @@ dbus_bool_t _dbus_connection_putback_message (DBusConnectio
dbus_bool_t _dbus_connection_remove_message (DBusConnection *connection,
DBusMessage *message);
+int _dbus_connection_get_n_incoming (DBusConnection *connection);
/* if DBUS_ENABLE_STATS */
void _dbus_connection_get_stats (DBusConnection *connection,
diff --git a/dbus/dbus-connection.c b/dbus/dbus-connection.c
index 618b956d..0c817aa2 100644
--- a/dbus/dbus-connection.c
+++ b/dbus/dbus-connection.c
@@ -587,8 +587,20 @@ void
_dbus_connection_queue_synthesized_message_link (DBusConnection *connection,
DBusList *link)
{
+ DBusMessage *msg, *rmsg;
+
HAVE_LOCK_CHECK (connection);
-
+
+ msg = (DBusMessage *)link->data;
+
+ rmsg = msg;
+ _dbus_transport_assure_protocol_version (connection->transport, &rmsg);
+
+ if (rmsg != msg) {
+ _dbus_list_free_link(link);
+ link = _dbus_list_alloc_link (rmsg);
+ }
+
_dbus_list_append_link (&connection->incoming_messages, link);
connection->n_incoming += 1;
@@ -2053,6 +2065,28 @@ _dbus_connection_send_preallocated_unlocked_no_update (DBusConnection *con
{
dbus_uint32_t serial;
+ /* Finish preparing the message */
+ if (dbus_message_get_serial (message) == 0)
+ {
+ serial = _dbus_connection_get_next_client_serial (connection);
+ dbus_message_set_serial (message, serial);
+ if (client_serial)
+ *client_serial = serial;
+ }
+ else
+ {
+ if (client_serial)
+ *client_serial = dbus_message_get_serial (message);
+ }
+
+ _dbus_verbose ("Message %p serial is %u\n",
+ message, dbus_message_get_serial (message));
+
+ dbus_message_lock (message);
+
+ /* This converts message if neccessary */
+ _dbus_transport_assure_protocol_version (connection->transport, &message);
+
preallocated->queue_link->data = message;
_dbus_list_prepend_link (&connection->outgoing_messages,
preallocated->queue_link);
@@ -2088,24 +2122,6 @@ _dbus_connection_send_preallocated_unlocked_no_update (DBusConnection *con
connection,
connection->n_outgoing);
- if (dbus_message_get_serial (message) == 0)
- {
- serial = _dbus_connection_get_next_client_serial (connection);
- dbus_message_set_serial (message, serial);
- if (client_serial)
- *client_serial = serial;
- }
- else
- {
- if (client_serial)
- *client_serial = dbus_message_get_serial (message);
- }
-
- _dbus_verbose ("Message %p serial is %u\n",
- message, dbus_message_get_serial (message));
-
- dbus_message_lock (message);
-
/* Now we need to run an iteration to hopefully just write the messages
* out immediately, and otherwise get them queued up
*/
@@ -2237,52 +2253,6 @@ _dbus_memory_pause_based_on_timeout (int timeout_milliseconds)
_dbus_sleep_milliseconds (1000);
}
-static DBusMessage *
-generate_local_error_message (dbus_uint32_t serial,
- char *error_name,
- char *error_msg)
-{
- DBusMessage *message;
- message = dbus_message_new (DBUS_MESSAGE_TYPE_ERROR);
- if (!message)
- goto out;
-
- if (!dbus_message_set_error_name (message, error_name))
- {
- dbus_message_unref (message);
- message = NULL;
- goto out;
- }
-
- dbus_message_set_no_reply (message, TRUE);
-
- if (!dbus_message_set_reply_serial (message,
- serial))
- {
- dbus_message_unref (message);
- message = NULL;
- goto out;
- }
-
- if (error_msg != NULL)
- {
- DBusMessageIter iter;
-
- dbus_message_iter_init_append (message, &iter);
- if (!dbus_message_iter_append_basic (&iter,
- DBUS_TYPE_STRING,
- &error_msg))
- {
- dbus_message_unref (message);
- message = NULL;
- goto out;
- }
- }
-
- out:
- return message;
-}
-
/*
* Peek the incoming queue to see if we got reply for a specific serial
*/
@@ -2525,9 +2495,9 @@ _dbus_connection_block_pending_call (DBusPendingCall *pending)
{
DBusMessage *error_msg;
- error_msg = generate_local_error_message (client_serial,
- DBUS_ERROR_DISCONNECTED,
- "Connection was disconnected before a reply was received");
+ error_msg = _dbus_generate_local_error_message (client_serial,
+ DBUS_ERROR_DISCONNECTED,
+ "Connection was disconnected before a reply was received");
/* on OOM error_msg is set to NULL */
complete_pending_call_and_unlock (connection, pending, error_msg);
@@ -5277,6 +5247,16 @@ dbus_connection_get_socket(DBusConnection *connection,
return retval;
}
+/**
+ *
+ * Getter for number of messages in incoming queue.
+ * Useful for sending reply to self (see kdbus_do_iteration)
+ */
+int
+_dbus_connection_get_n_incoming (DBusConnection *connection)
+{
+ return connection->n_incoming;
+}
/**
* Gets the UNIX user ID of the connection if known. Returns #TRUE if
diff --git a/dbus/dbus-marshal-gvariant.c b/dbus/dbus-marshal-gvariant.c
new file mode 100644
index 00000000..52595ed9
--- /dev/null
+++ b/dbus/dbus-marshal-gvariant.c
@@ -0,0 +1,1388 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-marshal-gvariant.c Marshalling routines for GVariant protocol
+ *
+ * Copyright (C) 2015 Samsung Electronics
+ *
+ * Licensed under the Academic Free License version 2.1
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include <config.h>
+#include "dbus-internals.h"
+#include "dbus-marshal-gvariant.h"
+#include "dbus-protocol-gvariant.h"
+#include "dbus-marshal-basic.h"
+#include "dbus-message-private.h"
+#include "dbus-signature.h"
+#include "dbus-connection-internal.h"
+#include <endian.h>
+
+/** Static #DBusString containing the signature of a message header */
+_DBUS_STRING_DEFINE_STATIC(_dbus_header_gvariant_signature_str, DBUS_HEADER_GVARIANT_SIGNATURE);
+
+#define FIELD_ID_SIZE sizeof(dbus_uint64_t)
+
+const DBusString *
+_dbus_get_gvariant_header_signature_str (void)
+{
+ return &_dbus_header_gvariant_signature_str;
+}
+
+static dbus_bool_t
+append_sized_value (DBusString *str,
+ size_t value,
+ size_t value_size)
+{
+ /* always write as little endian */
+ int i;
+ for (i = 0; i < value_size; i++)
+ {
+ size_t move = 8 * i;
+ size_t mask = 0xFF << move;
+ if (!_dbus_string_append_byte(str, (value & mask) >> move))
+ return FALSE;
+ }
+ return TRUE;
+}
+
+#define MAX_OFFSET_SIZE 8
+#define MAX_VALUE_FOR_OFFSET_SIZE(o) ((1ULL<<(8*(o)))-1)
+
+/* taken from systemd */
+static size_t
+bus_gvariant_determine_word_size(size_t sz, size_t extra)
+{
+ if (sz + extra <= 0xFF)
+ return 1;
+ else if (sz + extra*2 <= 0xFFFF)
+ return 2;
+ else if (sz + extra*4 <= 0xFFFFFFFF)
+ return 4;
+ else
+ return 8;
+}
+
+/* taken from systemd */
+static size_t
+bus_gvariant_read_word_le (const void *p, size_t sz)
+{
+ union {
+ uint16_t u16;
+ uint32_t u32;
+ uint64_t u64;
+ } x;
+
+ // FIXME
+// assert(p);
+
+ if (sz == 1)
+ return *(uint8_t*) p;
+
+ memcpy(&x, p, sz);
+
+ if (sz == 2)
+ return le16toh(x.u16);
+ else if (sz == 4)
+ return le32toh(x.u32);
+ else if (sz == 8)
+ return le64toh(x.u64);
+ return 0;
+}
+
+static const char *
+get_header_const_array (DBusHeader *header)
+{
+ return _dbus_string_get_const_data (&header->data) + FIRST_GVARIANT_FIELD_OFFSET;
+}
+
+static size_t
+get_header_array_size (DBusHeader *header)
+{
+ return _dbus_string_get_length (&header->data) - FIRST_GVARIANT_FIELD_OFFSET - header->padding;
+}
+
+static dbus_bool_t
+append_offsets (DBusString *str,
+ size_t *fields_offsets,
+ size_t n_fields_offsets)
+{
+ int i;
+ size_t array_size = _dbus_string_get_length (str) - FIRST_GVARIANT_FIELD_OFFSET;
+ size_t offset_size = bus_gvariant_determine_word_size (array_size, n_fields_offsets);
+
+ for (i = 0; i < n_fields_offsets; i++)
+ {
+ if (!append_sized_value (str, fields_offsets[i], offset_size))
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static dbus_bool_t
+append_field_string (DBusString *str,
+ dbus_uint64_t field,
+ const char *value,
+ char type,
+ size_t *fields_offsets,
+ size_t *n_fields_offsets)
+{
+ dbus_bool_t res = TRUE;
+ if (value != NULL)
+ {
+ res = res && _dbus_string_align_length(str, 8);
+ res = res && append_sized_value(str, field, FIELD_ID_SIZE);
+ res = res && _dbus_string_append_len(str, value, strlen(value)+1);
+ res = res && _dbus_string_append_byte(str, 0); /* variant value-signature separator */
+ res = res && _dbus_string_append_byte(str, type);
+ fields_offsets[(*n_fields_offsets)++] = _dbus_string_get_length(str) - FIRST_GVARIANT_FIELD_OFFSET;
+ }
+ return res;
+}
+
+static dbus_bool_t
+append_field_uint64 (DBusString *str,
+ dbus_uint64_t field,
+ dbus_uint64_t value,
+ size_t *fields_offsets,
+ size_t *n_fields_offsets)
+{
+ dbus_bool_t res = TRUE;
+ res = res && _dbus_string_align_length(str, 8);
+ res = res && append_sized_value(str, field, FIELD_ID_SIZE);
+ res = res && append_sized_value(str, value, 8);
+ res = res && _dbus_string_append_byte(str, 0); /* variant value-signature separator */
+ res = res && _dbus_string_append_byte(str, DBUS_TYPE_UINT64);
+ fields_offsets[(*n_fields_offsets)++] = _dbus_string_get_length(str) - FIRST_GVARIANT_FIELD_OFFSET;
+ return res;
+}
+
+static dbus_bool_t
+append_field_uint32 (DBusString *str,
+ dbus_uint64_t field,
+ dbus_uint32_t value,
+ size_t *fields_offsets,
+ size_t *n_fields_offsets)
+{
+ dbus_bool_t res = TRUE;
+ res = res && _dbus_string_align_length(str, 8);
+ res = res && append_sized_value(str, field, FIELD_ID_SIZE);
+ res = res && append_sized_value(str, value, 4);
+ res = res && _dbus_string_append_byte(str, 0); /* variant value-signature separator */
+ res = res && _dbus_string_append_byte(str, DBUS_TYPE_UINT32);
+
+ fields_offsets[(*n_fields_offsets)++] = _dbus_string_get_length(str) - FIRST_GVARIANT_FIELD_OFFSET;
+ return res;
+}
+
+static void
+_dbus_header_toggle_gvariant (DBusHeader *header, dbus_bool_t gvariant)
+{
+ header->protocol_version = gvariant ? DBUS_PROTOCOL_VERSION_GVARIANT : DBUS_MAJOR_PROTOCOL_VERSION;
+}
+
+static const char *
+get_next_field_address (const char *array_buffer, size_t offset)
+{
+ return array_buffer + _DBUS_ALIGN_VALUE(offset, 8);
+}
+
+static dbus_uint64_t
+get_field_after (const char *array_buffer, size_t offset)
+{
+ return *(dbus_uint64_t*)(get_next_field_address(array_buffer, offset));
+}
+
+static void
+_dbus_header_fill_cache (DBusHeader *header,
+ size_t *fields_offsets,
+ size_t n_fields_offsets)
+{
+ const char *array_buffer = get_header_const_array (header);
+ int i;
+
+ if (get_header_array_size (header) > 0)
+ {
+ header->fields[get_field_after (array_buffer, 0)].value_pos = FIELD_ID_SIZE + FIRST_GVARIANT_FIELD_OFFSET;
+ for (i=0; i < n_fields_offsets-1; i++)
+ {
+ dbus_uint64_t field = get_field_after (array_buffer, fields_offsets[i]);
+ header->fields[field].value_pos = _DBUS_ALIGN_VALUE(fields_offsets[i],8) +
+ FIELD_ID_SIZE + FIRST_GVARIANT_FIELD_OFFSET;
+ }
+ }
+}
+
+static dbus_bool_t
+correct_header_padding (DBusHeader *header)
+{
+ int unpadded_len = _dbus_string_get_length (&header->data);
+ if (!_dbus_string_align_length (&header->data, 8))
+ return FALSE;
+
+ header->padding = _dbus_string_get_length (&header->data) - unpadded_len;
+ return TRUE;
+}
+
+dbus_bool_t
+_dbus_header_gvariant_create (DBusHeader *header,
+ int byte_order,
+ int type,
+ const char *destination,
+ const char *path,
+ const char *interface,
+ const char *member,
+ const char *error_name)
+{
+ size_t fields_offsets[DBUS_HEADER_FIELD_LAST];
+ size_t n_fields_offsets = 0;
+ dbus_bool_t res = TRUE;
+
+ _dbus_assert (byte_order == DBUS_LITTLE_ENDIAN ||
+ byte_order == DBUS_BIG_ENDIAN);
+ _dbus_assert (((interface || type != DBUS_MESSAGE_TYPE_SIGNAL) && member) ||
+ (error_name) ||
+ !(interface || member || error_name));
+ _dbus_assert (_dbus_string_get_length (&header->data) == 0);
+
+ _dbus_header_toggle_gvariant (header, TRUE);
+
+ res = res && _dbus_string_append_byte (&header->data, byte_order);
+ res = res && _dbus_string_append_byte (&header->data, type);
+ res = res && _dbus_string_append_byte (&header->data, 0); /* flags */
+ res = res && _dbus_string_append_byte (&header->data, DBUS_PROTOCOL_VERSION_GVARIANT);
+ res = res && append_sized_value (&header->data, 0, sizeof(dbus_uint32_t)); /* reserved */
+ res = res && append_sized_value (&header->data, 0, sizeof(dbus_uint64_t)); /* cookie */
+ /* array of fields */
+ res = res && append_field_string (&header->data, DBUS_HEADER_FIELD_PATH, path, DBUS_TYPE_OBJECT_PATH,
+ fields_offsets, &n_fields_offsets);
+ res = res && append_field_string (&header->data, DBUS_HEADER_FIELD_DESTINATION, destination, DBUS_TYPE_STRING,
+ fields_offsets, &n_fields_offsets);
+ res = res && append_field_string (&header->data, DBUS_HEADER_FIELD_INTERFACE, interface, DBUS_TYPE_STRING,
+ fields_offsets, &n_fields_offsets);
+ res = res && append_field_string (&header->data, DBUS_HEADER_FIELD_MEMBER, member, DBUS_TYPE_STRING,
+ fields_offsets, &n_fields_offsets);
+ res = res && append_field_string (&header->data, DBUS_HEADER_FIELD_ERROR_NAME, error_name, DBUS_TYPE_STRING,
+ fields_offsets, &n_fields_offsets);
+ res = res && append_offsets (&header->data, fields_offsets, n_fields_offsets);
+
+ _dbus_header_fill_cache (header, fields_offsets, n_fields_offsets);
+ res = res && correct_header_padding (header);
+
+ return res;
+}
+
+static dbus_bool_t
+marshal_gvariant_string (DBusString *str,
+ int insert_at,
+ const char *value,
+ int *pos_after,
+ dbus_bool_t with_nul)
+{
+ DBusString value_str;
+ size_t value_len = strlen(value);
+
+ if (with_nul)
+ value_len++;
+
+ _dbus_string_init_const_len (&value_str, value, value_len);
+ if (!_dbus_string_copy_len (&value_str, 0, value_len, str, insert_at))
+ {
+ return FALSE;
+ }
+
+ if (pos_after)
+ *pos_after = insert_at + value_len;
+
+ return TRUE;
+}
+
+dbus_bool_t
+_dbus_marshal_write_gvariant_basic (DBusString *str,
+ int insert_at,
+ int type,
+ const void *value,
+ int byte_order,
+ int *pos_after)
+{
+ const DBusBasicValue *vp;
+ _dbus_assert (dbus_type_is_basic (type));
+
+ vp = value;
+
+ switch (type)
+ {
+ case DBUS_TYPE_STRING:
+ case DBUS_TYPE_OBJECT_PATH:
+ case DBUS_TYPE_SIGNATURE:
+ return marshal_gvariant_string (str, insert_at, vp->str, pos_after, TRUE);
+ case DBUS_TYPE_BOOLEAN:
+ if (pos_after)
+ (*pos_after)++;
+ return _dbus_string_insert_byte (str, insert_at, vp->u32 != FALSE);
+ default:
+ return _dbus_marshal_write_basic (str, insert_at, type, value, byte_order, pos_after);
+ }
+}
+
+void
+_dbus_marshal_read_gvariant_basic (const DBusString *str,
+ int pos,
+ int type,
+ void *value,
+ int byte_order,
+ int *new_pos)
+{
+ const char *str_data;
+
+ _dbus_assert (dbus_type_is_basic (type));
+
+ str_data = _dbus_string_get_const_data (str);
+ switch (type)
+ {
+ case DBUS_TYPE_STRING:
+ case DBUS_TYPE_OBJECT_PATH:
+ case DBUS_TYPE_SIGNATURE:
+ {
+ volatile char **vp = value;
+ *vp = (char*) str_data + pos;
+ pos += strlen (str_data+pos)+1;
+ }
+ break;
+ case DBUS_TYPE_BOOLEAN:
+ {
+ volatile dbus_bool_t *vp = value;
+ *vp = (dbus_bool_t) _dbus_string_get_byte (str, pos);
+ (pos)++;
+ }
+ break;
+ default:
+ _dbus_marshal_read_basic (str, pos, type, value, byte_order, new_pos);
+ break;
+ }
+
+ if (new_pos)
+ *new_pos = pos;
+}
+
+static void
+get_offsets (const char *buffer, size_t container_size,
+ size_t *fields_offsets, size_t *n_fields_offsets,
+ size_t *offset_size)
+{
+ *offset_size = bus_gvariant_determine_word_size (container_size, 0);
+
+ if (0 < container_size && 0 < *offset_size)
+ {
+ size_t last_offset_position = container_size - (*offset_size);
+ size_t last_offset = bus_gvariant_read_word_le (buffer + last_offset_position,
+ (*offset_size));
+ int i;
+
+ *n_fields_offsets = (container_size - last_offset) / (*offset_size);
+ fields_offsets[(*n_fields_offsets)-1] = last_offset;
+ for (i = 0; i < (*n_fields_offsets)-1; i++)
+ {
+ fields_offsets[i] = bus_gvariant_read_word_le (buffer + last_offset + i*(*offset_size),
+ (*offset_size));
+ }
+ }
+}
+
+static int
+find_field (int field, const char *array_buffer, size_t *fields_offsets, size_t n_fields_offsets,
+ size_t *field_offset)
+{
+ /* last_offset points to the offsets array, beyond the last element of the array container */
+ size_t last_offset = fields_offsets[n_fields_offsets-1];
+ int i = 0;
+ size_t next_offset = 0;
+
+ while ( next_offset < last_offset && get_field_after (array_buffer, next_offset) != field)
+ {
+ next_offset = fields_offsets[i];
+ i++;
+ }
+ if (next_offset < last_offset)
+ {
+ *field_offset = next_offset;
+ return i;
+ }
+ return -1;
+}
+
+dbus_bool_t
+_dbus_header_gvariant_delete_field (DBusHeader *header,
+ int field)
+{
+ size_t fields_offsets[DBUS_HEADER_FIELD_LAST];
+ size_t n_fields_offsets = 0;
+ size_t offset_size = 0;
+ const char *array_buffer;
+
+ _dbus_assert(field <= DBUS_HEADER_FIELD_LAST);
+
+ array_buffer = get_header_const_array (header);
+
+ get_offsets (array_buffer,
+ get_header_array_size (header),
+ fields_offsets, &n_fields_offsets, &offset_size );
+
+ if (0 < n_fields_offsets)
+ {
+ /* check if the field is already in the header */
+ size_t field_offset;
+ int field_index = find_field (field, array_buffer, fields_offsets, n_fields_offsets, &field_offset);
+
+ /* prepare for changing - remove array offsets and offsets */
+ _dbus_string_shorten (&header->data, n_fields_offsets*offset_size + header->padding);
+
+ if (field_index >= 0)
+ {
+ /* field exists */
+ size_t field_len = 0;
+ size_t field_start = 0;
+ /* let's remove aligned block of the field, along with padding */
+ if (field_index == 0)
+ {
+ field_len = _DBUS_ALIGN_VALUE (fields_offsets[0],8);
+ }
+ else
+ {
+ field_len = _DBUS_ALIGN_VALUE (fields_offsets[field_index],8) -
+ _DBUS_ALIGN_VALUE (fields_offsets[field_index-1],8);
+ }
+
+ field_start = FIRST_GVARIANT_FIELD_OFFSET + _DBUS_ALIGN_VALUE (field_offset, 8);
+
+ /* if this is the last field, then there is no padding at the end */
+ if (field_start + field_len > _dbus_string_get_length (&header->data))
+ {
+ field_len = _dbus_string_get_length (&header->data) - field_start;
+ }
+
+ /* remove the field */
+ _dbus_string_delete (&header->data, field_start, field_len);
+ header->fields[field].value_pos = _DBUS_HEADER_FIELD_VALUE_NONEXISTENT;
+ /* and update offsets */
+ for (; field_index < n_fields_offsets-1; field_index++)
+ {
+ fields_offsets[field_index] = fields_offsets[field_index+1]-field_len;
+ }
+ n_fields_offsets--;
+
+ /* remove padding from now-last field */
+ _dbus_string_shorten (&header->data,
+ _dbus_string_get_length(&header->data) -
+ (FIRST_GVARIANT_FIELD_OFFSET + fields_offsets[n_fields_offsets-1]));
+ header->padding = 0;
+ }
+ }
+
+ /* It seems impossible for append_offsets() and correct_header_padding() to fail,
+ because space for offsets was already allocated */
+ if (!append_offsets(&header->data, fields_offsets, n_fields_offsets))
+ return FALSE;
+ _dbus_header_fill_cache (header, fields_offsets, n_fields_offsets);
+ if (!correct_header_padding (header))
+ return FALSE;
+
+ return TRUE;
+}
+
+dbus_bool_t
+_dbus_header_set_field_basic_gvariant (DBusHeader *header,
+ int field,
+ int type,
+ const void *value)
+{
+ size_t fields_offsets[DBUS_HEADER_FIELD_LAST];
+ size_t n_fields_offsets = 0;
+ dbus_bool_t result = TRUE;
+ const DBusBasicValue *vp = value;
+ size_t offset_size = 0;
+ const char *array_buffer;
+
+ _dbus_assert(field != DBUS_HEADER_FIELD_INVALID);
+ _dbus_assert(field <= DBUS_HEADER_FIELD_LAST);
+
+ array_buffer = get_header_const_array (header);
+
+ result = result && _dbus_header_gvariant_delete_field (header, field);
+
+ /* now, we are sure that there is no such field (anymore) - so, simply append */
+
+ get_offsets (array_buffer,
+ get_header_array_size (header),
+ fields_offsets, &n_fields_offsets, &offset_size );
+
+ /* prepare for changing - remove array offsets and padding */
+ _dbus_string_shorten (&header->data, n_fields_offsets*offset_size + header->padding);
+
+ switch (type)
+ {
+ case DBUS_TYPE_STRING:
+ case DBUS_TYPE_OBJECT_PATH:
+ case DBUS_TYPE_SIGNATURE:
+ result = result && append_field_string (&header->data, field, vp->str, type,
+ fields_offsets, &n_fields_offsets);
+ break;
+ case DBUS_TYPE_UINT32:
+ result = result && append_field_uint32 (&header->data, field, vp->u32,
+ fields_offsets, &n_fields_offsets);
+ break;
+ case DBUS_TYPE_UINT64:
+ append_field_uint64 (&header->data, field, vp->u64,
+ fields_offsets, &n_fields_offsets);
+ result = TRUE;
+ break;
+ default:
+ _dbus_assert_not_reached("Not a basic type");
+ result = FALSE;
+ break;
+ }
+
+ result = result && append_offsets(&header->data, fields_offsets, n_fields_offsets);
+ _dbus_header_fill_cache (header, fields_offsets, n_fields_offsets);
+ result = result && correct_header_padding (header);
+
+ return result;
+}
+
+dbus_bool_t
+_dbus_header_get_field_basic_gvariant (DBusHeader *header,
+ int field,
+ int type,
+ void *value)
+{
+ size_t fields_offsets[DBUS_HEADER_FIELD_LAST];
+ size_t n_fields_offsets = 0;
+ dbus_bool_t result = FALSE;
+ DBusBasicValue *vp = value;
+ size_t offset_size = 0;
+ const char *array_buffer;
+
+ _dbus_assert(field != DBUS_HEADER_FIELD_INVALID);
+ _dbus_assert(field <= DBUS_HEADER_FIELD_LAST);
+
+ array_buffer = get_header_const_array (header);
+
+ get_offsets( array_buffer,
+ get_header_array_size (header),
+ fields_offsets, &n_fields_offsets, &offset_size );
+
+ if (0 < n_fields_offsets)
+ {
+ /* check if the field is already in the header */
+ size_t field_offset;
+ int field_index = find_field (field, array_buffer, fields_offsets, n_fields_offsets, &field_offset);
+ if (0 <= field_index)
+ {
+ /* field found, get value */
+ const void *field_begin = array_buffer + _DBUS_ALIGN_VALUE(field_offset,8) + FIELD_ID_SIZE;
+ dbus_uint32_t byte_order = _dbus_header_get_byte_order (header);
+
+ switch (type)
+ {
+ case DBUS_TYPE_STRING:
+ case DBUS_TYPE_OBJECT_PATH:
+ case DBUS_TYPE_SIGNATURE:
+ {
+ vp->str = (char *)field_begin;
+ }
+ break;
+ case DBUS_TYPE_UINT32:
+ {
+ vp->u32 = *(const dbus_uint32_t *)field_begin;
+ if (byte_order != DBUS_COMPILER_BYTE_ORDER)
+ vp->u32 = DBUS_UINT32_SWAP_LE_BE (vp->u32);
+ }
+ break;
+ case DBUS_TYPE_UINT64:
+ {
+ vp->u64 = *(const dbus_uint64_t *)field_begin;
+ if (byte_order != DBUS_COMPILER_BYTE_ORDER)
+ vp->u64 = DBUS_UINT64_SWAP_LE_BE (vp->u64);
+ }
+ break;
+ default:
+ _dbus_assert_not_reached("Not a basic type");
+ break;
+ }
+
+ result = TRUE;
+ }
+ }
+ return result;
+}
+
+void
+_dbus_marshal_skip_gvariant_basic (const DBusString *str,
+ int type,
+ int byte_order,
+ int *pos)
+{
+ switch (type)
+ {
+ case DBUS_TYPE_STRING:
+ case DBUS_TYPE_OBJECT_PATH:
+ case DBUS_TYPE_SIGNATURE:
+ /* FIXME - this will require redesign... size should come from upper container */
+ *pos += strlen (_dbus_string_get_const_data (str) + *pos) + 1; /* length plus nul */
+ break;
+ case DBUS_TYPE_BOOLEAN:
+ (*pos)++;
+ break;
+ default:
+ _dbus_marshal_skip_basic (str, type, byte_order, pos);
+ break;
+ }
+}
+
+dbus_bool_t
+_dbus_header_load_gvariant (DBusHeader *header,
+ DBusTypeReader *reader,
+ DBusValidity *validity)
+{
+ size_t fields_offsets[DBUS_HEADER_FIELD_LAST];
+ size_t n_fields_offsets = 0;
+ size_t offset_size = 0;
+ const char *array_buffer = get_header_const_array (header);
+
+ get_offsets( array_buffer,
+ get_header_array_size (header),
+ fields_offsets, &n_fields_offsets, &offset_size );
+
+ _dbus_header_fill_cache (header, fields_offsets, n_fields_offsets);
+ return TRUE;
+}
+
+dbus_bool_t
+_dbus_gvariant_raw_get_lengths (const DBusString *str,
+ dbus_uint32_t *fields_array_len_unsigned,
+ dbus_uint32_t *body_len_unsigned,
+ DBusValidity *validity)
+{
+ size_t message_len = _dbus_string_get_length (str);
+ size_t body_offsets_size = bus_gvariant_determine_word_size (message_len, 0);
+ const char *message_ptr = _dbus_string_get_const_data (str);
+ /* so, the offset of end of fields is written at offset str->len - body_offsets_size */
+ size_t end_of_fields = bus_gvariant_read_word_le (message_ptr + message_len - body_offsets_size,
+ body_offsets_size);
+ *fields_array_len_unsigned = end_of_fields - FIRST_GVARIANT_FIELD_OFFSET;
+
+ *body_len_unsigned = message_len - _DBUS_ALIGN_VALUE (end_of_fields, 8);
+ return TRUE;
+}
+
+DBusValidity
+_dbus_validate_gvariant_body_with_reason (const DBusString *expected_signature,
+ int expected_signature_start,
+ int byte_order,
+ int *bytes_remaining,
+ const DBusString *value_str,
+ int value_pos,
+ int len)
+{
+ /* FIXME stub */
+ if (bytes_remaining)
+ *bytes_remaining = 0;
+ return DBUS_VALID;
+}
+
+dbus_bool_t
+_dbus_message_gvariant_get_signature (DBusMessage *message,
+ const DBusString **type_str_p,
+ int *type_pos_p,
+ int *type_str_len)
+{
+ size_t body_len = _dbus_string_get_length (&message->body);
+ size_t message_len = _dbus_string_get_length (&message->header.data) + body_len;
+ size_t body_offsets_size = bus_gvariant_determine_word_size (message_len, 0);
+ const char *body_ptr = _dbus_string_get_const_data (&message->body);
+ const char *sig_end_ptr = body_ptr + body_len - body_offsets_size;
+ const char *sig_ptr = sig_end_ptr - 1;
+
+ while (sig_ptr >= body_ptr && (*sig_ptr) != 0)
+ {
+ sig_ptr--;
+ }
+
+ if (sig_ptr < body_ptr)
+ return FALSE;
+
+ if (type_str_p != NULL)
+ *type_str_p = &message->body;
+ *type_pos_p = sig_ptr - body_ptr + 1;
+ *type_str_len = sig_end_ptr - sig_ptr - 1;
+
+ return TRUE;
+}
+
+dbus_bool_t
+_dbus_message_append_body_offset (DBusMessage *message)
+{
+ size_t body_len = _dbus_string_get_length (&message->body);
+ size_t end_of_fields_offset = _dbus_string_get_length (&message->header.data) - message->header.padding;
+ size_t message_len = _dbus_string_get_length (&message->header.data) + body_len;
+ size_t body_offsets_size = bus_gvariant_determine_word_size (message_len, 1);
+
+ return append_sized_value (&message->body, end_of_fields_offset, body_offsets_size);
+}
+
+dbus_bool_t
+_dbus_message_gvariant_add_signature (DBusMessage *message,
+ const DBusString *type_str)
+{
+ dbus_bool_t res = _dbus_string_append_byte (&message->body, 0);
+ res = res && _dbus_string_append_byte (&message->body, '(');
+ res = res && marshal_gvariant_string (&message->body, _dbus_string_get_length (&message->body),
+ _dbus_string_get_const_data (type_str), NULL, FALSE);
+ res = res && _dbus_string_append_byte (&message->body, ')');
+ return res;
+}
+
+dbus_bool_t
+_dbus_message_gvariant_remove_body_offset (DBusMessage *message)
+{
+ size_t offset_size = bus_gvariant_determine_word_size (_dbus_string_get_length (&message->header.data) +
+ _dbus_string_get_length (&message->body),
+ 0);
+ _dbus_string_shorten (&message->body, offset_size);
+ return TRUE;
+}
+
+dbus_bool_t
+_dbus_message_finalize_gvariant (DBusMessage *message, dbus_bool_t remove_signature_from_header)
+{
+ DBusString str;
+ const DBusString *type_str;
+ int type_pos;
+ dbus_bool_t fieldSignaturePresent;
+ dbus_bool_t res = TRUE;
+
+ _dbus_assert (!message->locked);
+
+ if (message->header.protocol_version != DBUS_PROTOCOL_VERSION_GVARIANT)
+ return TRUE;
+
+ fieldSignaturePresent = _dbus_header_get_field_raw (&message->header,
+ DBUS_HEADER_FIELD_SIGNATURE,
+ &type_str,
+ &type_pos);
+ if (fieldSignaturePresent)
+ {
+ /* if there is signature field, then we need to move this signature to body,
+ * and delete the field
+ */
+ const char *sig_ptr = _dbus_string_get_const_data (type_str) + type_pos;
+ _dbus_string_init_const (&str, sig_ptr);
+ }
+ else
+ {
+ /* If there is no signature field, then the body is empty.
+ * However, we need to add signature anyway, because body is a variant.
+ */
+ _dbus_string_init_const (&str, "");
+ type_str = &str;
+ type_pos = 0;
+ /* Let's set the body also */
+ _dbus_string_set_length (&message->body, 0);
+ _dbus_string_append_byte (&message->body, 0);
+ }
+
+ res = res && _dbus_message_gvariant_add_signature (message, &str);
+
+ if (res && fieldSignaturePresent && remove_signature_from_header)
+ res = res && _dbus_header_gvariant_delete_field (&message->header, DBUS_HEADER_FIELD_SIGNATURE);
+
+ res = res && _dbus_message_append_body_offset (message);
+
+ return res;
+}
+
+/* returns length of the body inside the outermost variant
+ * that is, without offset and signature from the end of messages
+ */
+static size_t
+_dbus_message_gvariant_get_body_length (DBusMessage *message)
+{
+ size_t body_len = _dbus_string_get_length (&message->body);
+ size_t message_len = body_len + _dbus_string_get_length (&message->header.data);
+ body_len -= bus_gvariant_determine_word_size (message_len , 0);
+
+ while (body_len > 0 && _dbus_string_get_byte (&message->body, body_len) != 0)
+ body_len--;
+
+ return body_len;
+}
+
+static inline int
+get_max (int a, int b)
+{
+ return (a>b) ? a : b;
+}
+
+static int
+update_size (int current_size, int size_of_element, int *alignment, int new_alignment)
+{
+ *alignment = get_max (*alignment, new_alignment);
+ current_size = _DBUS_ALIGN_VALUE (current_size, *alignment);
+ return current_size + size_of_element;
+}
+
+static int
+_dbus_reader_get_signature_fixed_size (const DBusString *signature, int *pos, int *alignment)
+{
+ int res = 0;
+ int depth = 0;
+ int current_alignment = 1;
+ dbus_bool_t variable = FALSE;
+
+ char c = _dbus_string_get_byte (signature, *pos);
+ if (c == DBUS_STRUCT_BEGIN_CHAR || c == DBUS_DICT_ENTRY_BEGIN_CHAR)
+ {
+ depth = 1;
+ (*pos)++;
+ }
+
+ do {
+ switch (_dbus_string_get_byte (signature, *pos))
+ {
+ case DBUS_TYPE_BYTE:
+ case DBUS_TYPE_BOOLEAN:
+ res += 1;
+ break;
+ case DBUS_TYPE_INT16:
+ case DBUS_TYPE_UINT16:
+ res = update_size (res, 2, &current_alignment, 2);
+ break;
+ case DBUS_TYPE_INT32:
+ case DBUS_TYPE_UINT32:
+ case DBUS_TYPE_UNIX_FD:
+ res = update_size (res, 4, &current_alignment, 4);
+ break;
+ case DBUS_TYPE_INT64:
+ case DBUS_TYPE_UINT64:
+ case DBUS_TYPE_DOUBLE:
+ res = update_size (res, 8, &current_alignment, 8);
+ break;
+ case DBUS_STRUCT_END_CHAR:
+ case DBUS_DICT_ENTRY_END_CHAR:
+ depth--;
+ break;
+ case DBUS_STRUCT_BEGIN_CHAR:
+ case DBUS_DICT_ENTRY_BEGIN_CHAR:
+ {
+ int alignment_recursive;
+ int res_recursive = _dbus_reader_get_signature_fixed_size (signature, pos, &alignment_recursive);
+ if (res_recursive == 0)
+ variable = TRUE; /* variable size detected */
+
+ /* we need to update at least alignment */
+ res = update_size (res, res_recursive, &current_alignment, alignment_recursive);
+ }
+ break;
+ case DBUS_TYPE_VARIANT:
+ current_alignment = 8;
+ variable = TRUE;
+ break;
+ case DBUS_TYPE_ARRAY:
+ {
+ int alignment_recursive;
+ int recursive_pos = *pos + 1;
+ int res_recursive = _dbus_reader_get_signature_fixed_size (signature, &recursive_pos, &alignment_recursive);
+
+ variable = TRUE; /* variable size detected */
+
+ /* we need to update alignment */
+ res = update_size (res, res_recursive, &current_alignment, alignment_recursive);
+
+ /* and update position */
+ *pos = recursive_pos - 1;
+ }
+ break;
+ default:
+ variable = TRUE; /* variable size detected */
+ }
+ (*pos)++;
+ } while (depth > 0);
+
+ /* we want to point it to the last character, to allow upper instance to skip it */
+ (*pos)--;
+
+ if (alignment != NULL)
+ *alignment = current_alignment;
+
+ return variable ? 0 : res;
+}
+
+int
+_dbus_reader_get_type_fixed_size (DBusTypeReader *reader, int *alignment)
+{
+ int pos = reader->type_pos;
+ return _dbus_reader_get_signature_fixed_size (reader->type_str, &pos, alignment);
+}
+
+int
+_dbus_type_gvariant_get_fixed_size (const DBusString *type_str, int type_pos, int *alignment)
+{
+ return _dbus_reader_get_signature_fixed_size (type_str, &type_pos, alignment);
+}
+
+/* This is for structs and dict entries.
+ * Counts variable elements inside a container.
+ * This is equal to number of offsets embedded into the container.
+ */
+int
+_dbus_reader_count_offsets (const DBusTypeReader *reader)
+{
+ DBusTypeReader r;
+ int variables = 0;
+ dbus_bool_t prev_is_variable = FALSE;
+ int current_type;
+ int ending_char;
+
+ /* if signature is not empty, it must be after initial parenthesis */
+ /* empty signature has length 1 - only nul byte */
+ _dbus_assert (reader->type_pos > 0);
+
+ _dbus_type_reader_init_types_only (&r,
+ reader->type_str,
+ reader->type_pos);
+ r.gvariant = TRUE;
+ r.klass = reader->klass;
+
+ /* Check what container we're in */
+ switch (_dbus_string_get_byte (r.type_str, r.type_pos-1))
+ {
+ case DBUS_STRUCT_BEGIN_CHAR:
+ ending_char = DBUS_STRUCT_END_CHAR;
+ break;
+ case DBUS_DICT_ENTRY_BEGIN_CHAR:
+ ending_char = DBUS_DICT_ENTRY_END_CHAR;
+ break;
+ default:
+ _dbus_assert_not_reached ("function must be called inside structs or dict entries");
+ break;
+ }
+ r.finished = (_dbus_string_get_byte (r.type_str, r.type_pos) == ending_char);
+
+ while ((current_type = _dbus_type_reader_get_current_type (&r)) != DBUS_TYPE_INVALID)
+ {
+ int size = _dbus_reader_get_type_fixed_size (&r, NULL);
+ if (prev_is_variable)
+ variables++;
+ prev_is_variable = (size == 0);
+ _dbus_type_signature_next (_dbus_string_get_const_data(r.type_str), &r.type_pos);
+ r.finished = (_dbus_string_get_byte (r.type_str, r.type_pos) == ending_char);
+ }
+ return variables;
+}
+
+size_t
+_dbus_reader_get_offset_of_end_of_variable (DBusTypeReader *reader)
+{
+ if (reader->is_variant)
+ {
+ /* variant has its end set to the separating 0 */
+ return reader->value_end;
+ }
+ else
+ {
+ const char *buffer = _dbus_string_get_const_data (reader->value_str) + reader->value_start;
+ size_t container_size = reader->value_end - reader->value_start;
+ size_t offset_size = bus_gvariant_determine_word_size (container_size, 0);
+ int index_from_back = reader->offsets_from_back ?
+ reader->variable_index :
+ reader->n_offsets - 1 - reader->variable_index;
+
+ if (0 < container_size && 0 <= index_from_back)
+ {
+ size_t required_offset_position = container_size - (index_from_back+1)*offset_size;
+ if (index_from_back < reader->n_offsets)
+ return reader->value_start +
+ bus_gvariant_read_word_le (buffer + required_offset_position,
+ offset_size);
+ else if (reader->offsets_from_back)
+ return reader->value_start +
+ container_size - (reader->n_offsets * offset_size); /* this is end of internal container */
+ }
+ }
+
+ return reader->value_start;
+}
+
+int
+_dbus_reader_count_array_elems (const DBusTypeReader *reader)
+{
+ const char *buffer = _dbus_string_get_const_data (reader->value_str) + reader->value_start;
+ size_t container_size = reader->value_end - reader->value_start;
+ size_t offset_size = bus_gvariant_determine_word_size (container_size, 0);
+ size_t last_offset = bus_gvariant_read_word_le (buffer + container_size - offset_size, offset_size);
+ return (container_size - last_offset) / offset_size;
+}
+
+static dbus_bool_t
+write_offset (DBusString *offsets,
+ size_t offset,
+ size_t offset_size,
+ int insert_at)
+{
+ DBusString str;
+ dbus_bool_t res = _dbus_string_init_preallocated (&str, offset_size);
+ res = res && append_sized_value (&str, offset, offset_size);
+ res = res && _dbus_string_copy_len (&str, 0, offset_size, offsets, insert_at);
+ _dbus_string_free (&str);
+ return res;
+}
+
+static dbus_bool_t
+prepend_offset (DBusString *offsets,
+ size_t offset,
+ size_t offset_size)
+{
+ return write_offset (offsets, offset, offset_size, 0);
+}
+
+static dbus_bool_t
+append_offset (DBusString *offsets,
+ size_t offset,
+ size_t offset_size)
+{
+ return write_offset (offsets, offset, offset_size, _dbus_string_get_length(offsets));
+}
+
+static dbus_bool_t
+convert_offsets (DBusString *offsets,
+ size_t old_offsets_size,
+ size_t new_offsets_size)
+{
+ char *old_offsets = NULL;
+ size_t n_offsets = _dbus_string_get_length (offsets) / old_offsets_size;
+ dbus_bool_t result = _dbus_string_steal_data (offsets, &old_offsets);
+ int i;
+
+ for (i = 0; i < n_offsets && result; i++)
+ {
+ size_t offset = bus_gvariant_read_word_le (old_offsets + i*old_offsets_size, old_offsets_size);
+ result = result && append_sized_value (offsets, offset, new_offsets_size);
+ }
+
+ dbus_free (old_offsets);
+
+ return result;
+}
+
+static size_t
+get_offsets_count (DBusString *offsets, size_t offsets_size)
+{
+ return _dbus_string_get_length (offsets) / offsets_size;
+}
+
+static dbus_bool_t
+check_offsets_for_adding (DBusTypeWriter *writer)
+{
+ size_t container_size = writer->value_pos - writer->value_start;
+ size_t n_offsets = get_offsets_count (writer->offsets,
+ writer->offsets_size);
+ size_t offsets_size = bus_gvariant_determine_word_size (container_size, n_offsets + 1);
+ if (offsets_size != writer->offsets_size)
+ {
+ if (!convert_offsets (writer->offsets, writer->offsets_size, offsets_size))
+ return FALSE;
+ writer->offsets_size = offsets_size;
+ }
+ return TRUE;
+}
+
+static dbus_bool_t
+convert_offsets_in_body (DBusTypeWriter *writer,
+ size_t new_offsets_size)
+{
+ DBusString offsets;
+ size_t n_offsets;
+ int i;
+ dbus_bool_t result = _dbus_string_init (&offsets);
+ char *old_offsets;
+
+ result = result && _dbus_string_move (writer->value_str, writer->value_pos, &offsets, 0);
+ n_offsets = _dbus_string_get_length (&offsets) / writer->offsets_size;
+ old_offsets = _dbus_string_get_data (&offsets);
+
+ for (i = 0; i < n_offsets && result; i++)
+ {
+ size_t offset = bus_gvariant_read_word_le (old_offsets + i*writer->offsets_size, writer->offsets_size);
+ result = result && append_sized_value (writer->value_str, offset, new_offsets_size);
+ }
+
+ _dbus_string_free (&offsets);
+ return result;
+}
+
+static dbus_bool_t
+check_offsets_in_body_for_adding (DBusTypeWriter *writer)
+{
+ size_t container_size = writer->value_pos - writer->value_start;
+ size_t n_offsets = (_dbus_string_get_length (writer->value_str) - writer->value_pos) / writer->offsets_size;
+ size_t offsets_size = bus_gvariant_determine_word_size (container_size, n_offsets + 1);
+ if (offsets_size != writer->offsets_size)
+ {
+ if (!convert_offsets_in_body (writer, offsets_size))
+ return FALSE;
+ writer->offsets_size = offsets_size;
+ }
+ return TRUE;
+}
+
+static dbus_bool_t
+_dbus_writer_gvariant_add_offset_with_variability (DBusTypeWriter *writer,
+ dbus_bool_t fixed)
+{
+ writer->is_fixed = writer->is_fixed && fixed;
+
+ if (writer->body_container ||
+ DBUS_TYPE_STRUCT == writer->container_type ||
+ DBUS_TYPE_DICT_ENTRY == writer->container_type)
+ {
+ if (writer->u.struct_or_dict.last_offset != 0)
+ {
+ if (writer->body_container)
+ {
+ check_offsets_in_body_for_adding (writer);
+
+ write_offset (writer->value_str,
+ writer->u.struct_or_dict.last_offset,
+ writer->offsets_size,
+ writer->value_pos);
+ }
+ else
+ {
+ check_offsets_for_adding (writer);
+
+ prepend_offset (writer->offsets,
+ writer->u.struct_or_dict.last_offset,
+ writer->offsets_size);
+ }
+ }
+ if (!fixed)
+ {
+ writer->u.struct_or_dict.last_offset = writer->value_pos - writer->value_start;
+ }
+ else
+ {
+ writer->u.struct_or_dict.last_offset = 0;
+ }
+ }
+ else if (DBUS_TYPE_ARRAY == writer->container_type)
+ {
+ if (writer->offsets_size > 0)
+ {
+ check_offsets_for_adding (writer);
+
+ if (!append_offset (writer->offsets,
+ writer->value_pos - writer->value_start,
+ writer->offsets_size))
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+static dbus_bool_t
+_dbus_writer_gvariant_add_offset (DBusTypeWriter *writer,
+ int type)
+{
+ return _dbus_writer_gvariant_add_offset_with_variability (writer, dbus_type_is_fixed (type));
+}
+
+/* this function gets only known alignments - other are 1 */
+static int
+get_alignment (int type)
+{
+ switch (type)
+ {
+ case DBUS_TYPE_INT16:
+ case DBUS_TYPE_UINT16:
+ return 2;
+ case DBUS_TYPE_INT32:
+ case DBUS_TYPE_UINT32:
+ case DBUS_TYPE_UNIX_FD:
+ return 4;
+ case DBUS_TYPE_INT64:
+ case DBUS_TYPE_UINT64:
+ case DBUS_TYPE_DOUBLE:
+ case DBUS_TYPE_VARIANT:
+ return 8;
+ default:
+ break;
+ }
+ return 1;
+}
+
+static dbus_bool_t
+fix_struct_alignment_value (DBusTypeWriter *writer, int alignment)
+{
+ dbus_bool_t result = TRUE;
+ int old_alignment = writer->alignment;
+ if (old_alignment < alignment)
+ {
+ int diff = _DBUS_ALIGN_VALUE (writer->value_start, alignment) - writer->value_start;
+ result = _dbus_string_insert_bytes (writer->value_str, writer->value_start, diff, 0);
+ writer->value_start += diff;
+ writer->value_pos += diff;
+ writer->alignment = alignment;
+ }
+ return result;
+}
+
+static dbus_bool_t
+fix_struct_alignment (DBusTypeWriter *writer, int type)
+{
+ return fix_struct_alignment_value (writer, get_alignment (type));
+}
+
+dbus_bool_t
+_dbus_type_writer_gvariant_write_basic_no_typecode (DBusTypeWriter *writer,
+ int type,
+ const void *value)
+{
+ dbus_bool_t result = TRUE;
+
+ if (writer->container_type == DBUS_TYPE_STRUCT || writer->container_type == DBUS_TYPE_DICT_ENTRY)
+ result = fix_struct_alignment (writer, type);
+
+ result = result && _dbus_marshal_write_gvariant_basic (writer->value_str,
+ writer->value_pos,
+ type,
+ value,
+ writer->byte_order,
+ &writer->value_pos);
+
+ result = result && _dbus_writer_gvariant_add_offset (writer, type);
+ return result;
+}
+
+static dbus_bool_t
+write_offsets (DBusString *dest, size_t insert_at, DBusString *offsets)
+{
+ return _dbus_string_copy (offsets, 0, dest, insert_at);
+}
+
+dbus_bool_t
+_dbus_writer_unrecurse_gvariant_write (DBusTypeWriter *writer,
+ DBusTypeWriter *sub)
+{
+ dbus_bool_t result = TRUE;
+
+ if (writer->alignment < sub->alignment)
+ writer->alignment = sub->alignment;
+
+ switch (sub->container_type) {
+ case DBUS_TYPE_STRUCT:
+ case DBUS_TYPE_DICT_ENTRY:
+ {
+ int diff;
+ int sub_len;
+
+ if (NULL != sub->offsets)
+ {
+ write_offsets (sub->value_str, sub->value_pos, sub->offsets);
+
+ _dbus_string_free (sub->offsets);
+ dbus_free (sub->offsets);
+ }
+
+ diff = _DBUS_ALIGN_VALUE (writer->value_pos, sub->alignment) - writer->value_pos;
+
+ result = _dbus_string_insert_bytes (writer->value_str, writer->value_pos, diff, 0);
+ writer->value_pos += diff;
+ sub_len = _dbus_string_get_length (sub->value_str);
+ result = result && _dbus_string_copy_len (sub->value_str, 0,
+ sub_len,
+ writer->value_str,
+ writer->value_pos);
+ writer->value_pos += sub_len;
+
+ _dbus_string_free (sub->value_str);
+ dbus_free (sub->value_str);
+
+ break;
+ }
+ case DBUS_TYPE_VARIANT:
+ {
+ int sub_type_len;
+
+ /* write separating nul byte */
+ result = _dbus_string_insert_byte (sub->value_str, sub->value_pos, 0);
+ sub->value_pos += 1;
+
+ /* write signature */
+ sub_type_len = _dbus_string_get_length (sub->type_str);
+ result = result && _dbus_string_copy_len (sub->type_str, 0,
+ sub_type_len,
+ sub->value_str,
+ sub->value_pos);
+ sub->value_pos += sub_type_len;
+
+ /* free type string allocated in writer_recurse_variant() */
+ _dbus_string_free (sub->type_str);
+ dbus_free (sub->type_str);
+
+ /* update parent's string pointer */
+ writer->value_pos = sub->value_pos;
+
+ break;
+ }
+ case DBUS_TYPE_ARRAY:
+ writer->value_pos = sub->value_pos;
+ if (NULL != sub->offsets)
+ {
+ write_offsets (sub->value_str, sub->value_pos, sub->offsets);
+
+ writer->value_pos += _dbus_string_get_length (sub->offsets);
+
+ _dbus_string_free (sub->offsets);
+ dbus_free (sub->offsets);
+ }
+
+ break;
+ default:
+ _dbus_assert_not_reached("Invalid container type");
+ }
+
+ /* well, we don't know where in the type string beginning of current container is */
+ result = result && _dbus_writer_gvariant_add_offset_with_variability (writer, sub->is_fixed);
+
+ return result;
+}
+
+void
+_dbus_type_reader_gvariant_init (DBusTypeReader *reader,
+ DBusMessage *message)
+{
+ reader->gvariant = TRUE;
+ /* GVariant wraps contents into struct */
+ if (_dbus_string_get_byte (reader->type_str, reader->type_pos) == DBUS_STRUCT_BEGIN_CHAR)
+ {
+ reader->type_pos++;
+ if (_dbus_string_get_byte (reader->type_str, reader->type_pos) == DBUS_STRUCT_END_CHAR)
+ reader->finished = TRUE;
+ }
+
+ reader->value_end = _dbus_message_gvariant_get_body_length (message);
+ reader->n_offsets = _dbus_reader_count_offsets (reader);
+}
diff --git a/dbus/dbus-marshal-gvariant.h b/dbus/dbus-marshal-gvariant.h
new file mode 100644
index 00000000..79d23a0a
--- /dev/null
+++ b/dbus/dbus-marshal-gvariant.h
@@ -0,0 +1,132 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-marshal-gvariant.h Managing GVariant marshaling/demarshaling of messages
+ *
+ * Copyright (C) 2015 Samsung Electronics
+ *
+ * Licensed under the Academic Free License version 2.1
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifndef DBUS_MARSHAL_GVARIANT_H
+#define DBUS_MARSHAL_GVARIANT_H
+
+#include <dbus/dbus-marshal-header.h>
+#include <dbus/dbus-marshal-recursive.h>
+#include <dbus/dbus-message.h>
+#include <dbus/dbus-connection.h>
+
+const DBusString *_dbus_get_gvariant_header_signature_str (void);
+
+dbus_bool_t _dbus_header_gvariant_create (DBusHeader *header,
+ int byte_order,
+ int type,
+ const char *destination,
+ const char *path,
+ const char *interface,
+ const char *member,
+ const char *error_name);
+
+dbus_bool_t _dbus_type_writer_write_gvariant_basic (DBusTypeWriter *writer,
+ int type,
+ const void *value);
+
+dbus_bool_t _dbus_marshal_write_gvariant_basic (DBusString *str,
+ int insert_at,
+ int type,
+ const void *value,
+ int byte_order,
+ int *pos_after);
+
+dbus_bool_t _dbus_header_set_field_basic_gvariant (DBusHeader *header,
+ int field,
+ int type,
+ const void *value);
+
+dbus_bool_t _dbus_header_get_field_basic_gvariant (DBusHeader *header,
+ int field,
+ int type,
+ void *value);
+
+dbus_bool_t _dbus_header_gvariant_delete_field (DBusHeader *header,
+ int field);
+
+void _dbus_marshal_read_gvariant_basic (const DBusString *str,
+ int pos,
+ int type,
+ void *value,
+ int byte_order,
+ int *new_pos);
+
+void _dbus_marshal_skip_gvariant_basic (const DBusString *str,
+ int type,
+ int byte_order,
+ int *pos);
+
+dbus_bool_t _dbus_header_load_gvariant (DBusHeader *header,
+ DBusTypeReader *reader,
+ DBusValidity *validity);
+
+dbus_bool_t _dbus_gvariant_raw_get_lengths (const DBusString *str,
+ dbus_uint32_t *fields_array_len_unsigned,
+ dbus_uint32_t *body_len_unsigned,
+ DBusValidity *validity);
+
+DBusValidity _dbus_validate_gvariant_body_with_reason (const DBusString *expected_signature,
+ int expected_signature_start,
+ int byte_order,
+ int *bytes_remaining,
+ const DBusString *value_str,
+ int value_pos,
+ int len);
+
+dbus_bool_t _dbus_message_gvariant_get_signature (DBusMessage *message,
+ const DBusString **type_str_p,
+ int *type_pos_p,
+ int *type_str_len);
+
+dbus_bool_t _dbus_message_gvariant_add_signature (DBusMessage *message,
+ const DBusString *type_str);
+
+dbus_bool_t _dbus_message_append_body_offset (DBusMessage *message);
+dbus_bool_t _dbus_message_gvariant_remove_body_offset (DBusMessage *message);
+
+dbus_bool_t _dbus_message_finalize_gvariant (DBusMessage *message,
+ dbus_bool_t remove_signature_from_header);
+
+size_t _dbus_reader_get_offset_of_end_of_variable (DBusTypeReader *reader);
+int _dbus_reader_get_type_fixed_size (DBusTypeReader *reader,
+ int *alignment);
+
+int _dbus_type_gvariant_get_fixed_size (const DBusString *type_str,
+ int type_pos,
+ int *alignment);
+
+int _dbus_reader_count_offsets (const DBusTypeReader *reader);
+
+int _dbus_reader_count_array_elems (const DBusTypeReader *reader);
+
+dbus_bool_t _dbus_type_writer_gvariant_write_basic_no_typecode (DBusTypeWriter *writer,
+ int type,
+ const void *value);
+
+dbus_bool_t _dbus_writer_unrecurse_gvariant_write (DBusTypeWriter *writer,
+ DBusTypeWriter *sub);
+
+void _dbus_type_reader_gvariant_init (DBusTypeReader *reader,
+ DBusMessage *message);
+
+#endif /* DBUS_MARSHAL_GVARIANT_H */
diff --git a/dbus/dbus-marshal-header.c b/dbus/dbus-marshal-header.c
index 48151c62..bd98a12d 100644
--- a/dbus/dbus-marshal-header.c
+++ b/dbus/dbus-marshal-header.c
@@ -27,6 +27,9 @@
#include "dbus-marshal-recursive.h"
#include "dbus-marshal-byteswap.h"
+#include "dbus-protocol-gvariant.h"
+#include "dbus-marshal-gvariant.h"
+
/**
* @addtogroup DBusMarshal
*
@@ -123,6 +126,12 @@ correct_header_padding (DBusHeader *header)
#define HEADER_END_BEFORE_PADDING(header) \
(_dbus_string_get_length (&(header)->data) - (header)->padding)
+static dbus_bool_t
+_dbus_header_is_gvariant (const DBusHeader *header)
+{
+ return (header->protocol_version == DBUS_PROTOCOL_VERSION_GVARIANT);
+}
+
/**
* Invalidates all fields in the cache. This may be used when the
* cache is totally uninitialized (contains junk) so should not
@@ -415,6 +424,11 @@ _dbus_header_set_serial (DBusHeader *header,
SERIAL_OFFSET,
serial,
_dbus_header_get_byte_order (header));
+ if (_dbus_header_is_gvariant (header))
+ _dbus_marshal_set_uint32 (&header->data,
+ SERIAL_OFFSET+4,
+ 0,
+ _dbus_header_get_byte_order (header));
}
/**
@@ -513,6 +527,7 @@ _dbus_header_copy (const DBusHeader *header,
* for use. #NULL may be specified for some or all of the fields to
* avoid adding those fields. Some combinations of fields don't make
* sense, and passing them in will trigger an assertion failure.
+ * This is used only for dbus1 messages. GVariant uses _dbus_header_gvariant_create.
*
* @param header the header
* @param byte_order byte order of the header
@@ -678,13 +693,16 @@ _dbus_header_have_message_untrusted (int max_message_length,
int *body_len,
const DBusString *str,
int start,
- int len)
+ int len,
+ dbus_bool_t *is_gvariant)
{
dbus_uint32_t header_len_unsigned;
dbus_uint32_t fields_array_len_unsigned;
dbus_uint32_t body_len_unsigned;
+ dbus_uint32_t protocol_version;
+
_dbus_assert (start >= 0);
_dbus_assert (start < _DBUS_INT32_MAX / 2);
_dbus_assert (len >= 0);
@@ -699,9 +717,32 @@ _dbus_header_have_message_untrusted (int max_message_length,
return FALSE;
}
- _dbus_assert (FIELDS_ARRAY_LENGTH_OFFSET + 4 <= len);
- fields_array_len_unsigned = _dbus_marshal_read_uint32 (str, start + FIELDS_ARRAY_LENGTH_OFFSET,
- *byte_order, NULL);
+ protocol_version = _dbus_string_get_byte (str, start + VERSION_OFFSET);
+ if (DBUS_MAJOR_PROTOCOL_VERSION == protocol_version)
+ {
+ _dbus_assert (FIELDS_ARRAY_LENGTH_OFFSET + 4 <= len);
+ fields_array_len_unsigned = _dbus_marshal_read_uint32 (str, start + FIELDS_ARRAY_LENGTH_OFFSET,
+ *byte_order, NULL);
+
+ _dbus_assert (BODY_LENGTH_OFFSET + 4 < len);
+ body_len_unsigned = _dbus_marshal_read_uint32 (str, start + BODY_LENGTH_OFFSET,
+ *byte_order, NULL);
+
+ *is_gvariant = FALSE;
+ }
+ else if (DBUS_PROTOCOL_VERSION_GVARIANT == protocol_version)
+ {
+ if (!_dbus_gvariant_raw_get_lengths (str, &fields_array_len_unsigned, &body_len_unsigned, validity))
+ {
+ return FALSE;
+ }
+ *is_gvariant = TRUE;
+ }
+ else
+ {
+ *validity = DBUS_INVALID_BAD_PROTOCOL_VERSION;
+ return FALSE;
+ }
if (fields_array_len_unsigned > (unsigned) max_message_length)
{
@@ -709,10 +750,6 @@ _dbus_header_have_message_untrusted (int max_message_length,
return FALSE;
}
- _dbus_assert (BODY_LENGTH_OFFSET + 4 < len);
- body_len_unsigned = _dbus_marshal_read_uint32 (str, start + BODY_LENGTH_OFFSET,
- *byte_order, NULL);
-
if (body_len_unsigned > (unsigned) max_message_length)
{
*validity = DBUS_INVALID_INSANE_BODY_LENGTH;
@@ -940,6 +977,86 @@ load_and_validate_field (DBusHeader *header,
return DBUS_VALID;
}
+static dbus_bool_t
+_dbus_header_load_dbus1 (DBusHeader *header,
+ DBusTypeReader *reader,
+ DBusValidity *validity,
+ int body_len)
+{
+ dbus_uint32_t v_uint32;
+ dbus_uint32_t serial;
+ DBusTypeReader array_reader;
+
+ /* BODY LENGTH */
+ _dbus_assert (_dbus_type_reader_get_current_type (reader) == DBUS_TYPE_UINT32);
+ _dbus_assert (_dbus_type_reader_get_value_pos (reader) == BODY_LENGTH_OFFSET);
+ _dbus_type_reader_read_basic (reader, &v_uint32);
+ _dbus_type_reader_next (reader);
+
+ _dbus_assert (body_len == (signed) v_uint32);
+
+ /* SERIAL */
+ _dbus_assert (_dbus_type_reader_get_current_type (reader) == DBUS_TYPE_UINT32);
+ _dbus_assert (_dbus_type_reader_get_value_pos (reader) == SERIAL_OFFSET);
+ _dbus_type_reader_read_basic (reader, &serial);
+ _dbus_type_reader_next (reader);
+
+ if (serial == 0)
+ {
+ *validity = DBUS_INVALID_BAD_SERIAL;
+ return FALSE;
+ }
+
+ _dbus_assert (_dbus_type_reader_get_current_type (reader) == DBUS_TYPE_ARRAY);
+ _dbus_assert (_dbus_type_reader_get_value_pos (reader) == FIELDS_ARRAY_LENGTH_OFFSET);
+
+ _dbus_type_reader_recurse (reader, &array_reader);
+ while (_dbus_type_reader_get_current_type (&array_reader) != DBUS_TYPE_INVALID)
+ {
+ DBusTypeReader struct_reader;
+ DBusTypeReader variant_reader;
+ unsigned char field_code;
+ DBusValidity v;
+
+ _dbus_assert (_dbus_type_reader_get_current_type (&array_reader) == DBUS_TYPE_STRUCT);
+
+ _dbus_type_reader_recurse (&array_reader, &struct_reader);
+
+ _dbus_assert (_dbus_type_reader_get_current_type (&struct_reader) == DBUS_TYPE_BYTE);
+ _dbus_type_reader_read_basic (&struct_reader, &field_code);
+ _dbus_type_reader_next (&struct_reader);
+
+ if (field_code == DBUS_HEADER_FIELD_INVALID)
+ {
+ _dbus_verbose ("invalid header field code\n");
+ *validity = DBUS_INVALID_HEADER_FIELD_CODE;
+ return FALSE;
+ }
+
+ if (field_code > DBUS_HEADER_FIELD_LAST)
+ {
+ _dbus_verbose ("unknown header field code %d, skipping\n",
+ field_code);
+ goto next_field;
+ }
+
+ _dbus_assert (_dbus_type_reader_get_current_type (&struct_reader) == DBUS_TYPE_VARIANT);
+ _dbus_type_reader_recurse (&struct_reader, &variant_reader);
+
+ v = load_and_validate_field (header, field_code, &variant_reader);
+ if (v != DBUS_VALID)
+ {
+ _dbus_verbose ("Field %d was invalid\n", field_code);
+ *validity = v;
+ return FALSE;
+ }
+
+ next_field:
+ _dbus_type_reader_next (&array_reader);
+ }
+ return TRUE;
+}
+
/**
* Creates a message header from potentially-untrusted data. The
* return value is #TRUE if there was enough memory and the data was
@@ -981,13 +1098,11 @@ _dbus_header_load (DBusHeader *header,
int leftover;
DBusValidity v;
DBusTypeReader reader;
- DBusTypeReader array_reader;
unsigned char v_byte;
- dbus_uint32_t v_uint32;
- dbus_uint32_t serial;
int padding_start;
int padding_len;
int i;
+ const DBusString *signature;
_dbus_assert (start == (int) _DBUS_ALIGN_VALUE (start, 8));
_dbus_assert (header_len <= len);
@@ -999,6 +1114,14 @@ _dbus_header_load (DBusHeader *header,
*validity = DBUS_VALIDITY_UNKNOWN_OOM_ERROR;
return FALSE;
}
+ if (_dbus_header_is_gvariant (header))
+ {
+ signature = _dbus_get_gvariant_header_signature_str();
+ }
+ else
+ {
+ signature = &_dbus_header_signature_str;
+ }
if (mode == DBUS_VALIDATION_MODE_WE_TRUST_THIS_DATA_ABSOLUTELY)
{
@@ -1006,10 +1129,20 @@ _dbus_header_load (DBusHeader *header,
}
else
{
- v = _dbus_validate_body_with_reason (&_dbus_header_signature_str, 0,
- byte_order,
- &leftover,
- str, start, len);
+ if (!_dbus_header_is_gvariant (header))
+ {
+ v = _dbus_validate_body_with_reason (signature, 0,
+ byte_order,
+ &leftover,
+ str, start, len);
+ }
+ else
+ {
+ v = _dbus_validate_gvariant_body_with_reason (signature, 0,
+ byte_order,
+ &leftover,
+ str, start, len);
+ }
if (v != DBUS_VALID)
{
@@ -1048,7 +1181,7 @@ _dbus_header_load (DBusHeader *header,
_dbus_type_reader_init (&reader,
byte_order,
- &_dbus_header_signature_str, 0,
+ signature, 0,
str, start);
/* BYTE ORDER */
@@ -1090,75 +1223,25 @@ _dbus_header_load (DBusHeader *header,
if (v_byte != DBUS_MAJOR_PROTOCOL_VERSION)
{
- *validity = DBUS_INVALID_BAD_PROTOCOL_VERSION;
- goto invalid;
+ if (v_byte == DBUS_PROTOCOL_VERSION_GVARIANT)
+ {
+ reader.gvariant = TRUE;
+ }
+ else
+ {
+ *validity = DBUS_INVALID_BAD_PROTOCOL_VERSION;
+ goto invalid;
+ }
}
-
- /* BODY LENGTH */
- _dbus_assert (_dbus_type_reader_get_current_type (&reader) == DBUS_TYPE_UINT32);
- _dbus_assert (_dbus_type_reader_get_value_pos (&reader) == BODY_LENGTH_OFFSET);
- _dbus_type_reader_read_basic (&reader, &v_uint32);
- _dbus_type_reader_next (&reader);
-
- _dbus_assert (body_len == (signed) v_uint32);
-
- /* SERIAL */
- _dbus_assert (_dbus_type_reader_get_current_type (&reader) == DBUS_TYPE_UINT32);
- _dbus_assert (_dbus_type_reader_get_value_pos (&reader) == SERIAL_OFFSET);
- _dbus_type_reader_read_basic (&reader, &serial);
- _dbus_type_reader_next (&reader);
-
- if (serial == 0)
+ if (reader.gvariant)
{
- *validity = DBUS_INVALID_BAD_SERIAL;
- goto invalid;
+ if (!_dbus_header_load_gvariant (header, &reader, validity))
+ goto invalid;
}
-
- _dbus_assert (_dbus_type_reader_get_current_type (&reader) == DBUS_TYPE_ARRAY);
- _dbus_assert (_dbus_type_reader_get_value_pos (&reader) == FIELDS_ARRAY_LENGTH_OFFSET);
-
- _dbus_type_reader_recurse (&reader, &array_reader);
- while (_dbus_type_reader_get_current_type (&array_reader) != DBUS_TYPE_INVALID)
+ else
{
- DBusTypeReader struct_reader;
- DBusTypeReader variant_reader;
- unsigned char field_code;
-
- _dbus_assert (_dbus_type_reader_get_current_type (&array_reader) == DBUS_TYPE_STRUCT);
-
- _dbus_type_reader_recurse (&array_reader, &struct_reader);
-
- _dbus_assert (_dbus_type_reader_get_current_type (&struct_reader) == DBUS_TYPE_BYTE);
- _dbus_type_reader_read_basic (&struct_reader, &field_code);
- _dbus_type_reader_next (&struct_reader);
-
- if (field_code == DBUS_HEADER_FIELD_INVALID)
- {
- _dbus_verbose ("invalid header field code\n");
- *validity = DBUS_INVALID_HEADER_FIELD_CODE;
- goto invalid;
- }
-
- if (field_code > DBUS_HEADER_FIELD_LAST)
- {
- _dbus_verbose ("unknown header field code %d, skipping\n",
- field_code);
- goto next_field;
- }
-
- _dbus_assert (_dbus_type_reader_get_current_type (&struct_reader) == DBUS_TYPE_VARIANT);
- _dbus_type_reader_recurse (&struct_reader, &variant_reader);
-
- v = load_and_validate_field (header, field_code, &variant_reader);
- if (v != DBUS_VALID)
- {
- _dbus_verbose ("Field %d was invalid\n", field_code);
- *validity = v;
- goto invalid;
- }
-
- next_field:
- _dbus_type_reader_next (&array_reader);
+ if (!_dbus_header_load_dbus1 (header, &reader, validity, body_len))
+ goto invalid;
}
/* Anything we didn't fill in is now known not to exist */
@@ -1258,19 +1341,8 @@ find_field_for_modification (DBusHeader *header,
return retval;
}
-/**
- * Sets the value of a field with basic type. If the value is a string
- * value, it isn't allowed to be #NULL. If the field doesn't exist,
- * it will be created.
- *
- * @param header the header
- * @param field the field to set
- * @param type the type of the value
- * @param value the value as for _dbus_marshal_set_basic()
- * @returns #FALSE if no memory
- */
-dbus_bool_t
-_dbus_header_set_field_basic (DBusHeader *header,
+static dbus_bool_t
+_dbus_header_set_field_basic_dbus1 (DBusHeader *header,
int field,
int type,
const void *value)
@@ -1338,17 +1410,29 @@ _dbus_header_set_field_basic (DBusHeader *header,
}
/**
- * Gets the value of a field with basic type. If the field
- * doesn't exist, returns #FALSE, otherwise returns #TRUE.
+ * Sets the value of a field with basic type. If the value is a string
+ * value, it isn't allowed to be #NULL. If the field doesn't exist,
+ * it will be created.
*
* @param header the header
- * @param field the field to get
+ * @param field the field to set
* @param type the type of the value
- * @param value the value as for _dbus_marshal_read_basic()
- * @returns #FALSE if the field doesn't exist
+ * @param value the value as for _dbus_marshal_set_basic()
+ * @returns #FALSE if no memory
*/
dbus_bool_t
-_dbus_header_get_field_basic (DBusHeader *header,
+_dbus_header_set_field_basic (DBusHeader *header,
+ int field,
+ int type,
+ const void *value)
+{
+ return _dbus_header_is_gvariant (header) ?
+ _dbus_header_set_field_basic_gvariant (header, field, type, value) :
+ _dbus_header_set_field_basic_dbus1 (header, field, type, value);
+}
+
+static dbus_bool_t
+_dbus_header_get_field_basic_dbus1 (DBusHeader *header,
int field,
int type,
void *value)
@@ -1374,6 +1458,26 @@ _dbus_header_get_field_basic (DBusHeader *header,
return TRUE;
}
+/**
+ * Gets the value of a field with basic type. If the field
+ * doesn't exist, returns #FALSE, otherwise returns #TRUE.
+ *
+ * @param header the header
+ * @param field the field to get
+ * @param type the type of the value
+ * @param value the value as for _dbus_marshal_read_basic()
+ * @returns #FALSE if the field doesn't exist
+ */
+dbus_bool_t
+_dbus_header_get_field_basic (DBusHeader *header,
+ int field,
+ int type,
+ void *value)
+{
+ return _dbus_header_is_gvariant (header) ?
+ _dbus_header_get_field_basic_gvariant (header, field, type, value) :
+ _dbus_header_get_field_basic_dbus1 (header, field, type, value);
+}
/**
* Gets the raw marshaled data for a field. If the field doesn't
diff --git a/dbus/dbus-marshal-header.h b/dbus/dbus-marshal-header.h
index 350fe5c4..4ff986c9 100644
--- a/dbus/dbus-marshal-header.h
+++ b/dbus/dbus-marshal-header.h
@@ -57,6 +57,7 @@ struct DBusHeader
dbus_uint32_t padding : 3; /**< bytes of alignment in header */
dbus_uint32_t byte_order : 8; /**< byte order of header */
+ unsigned char protocol_version;
};
dbus_bool_t _dbus_header_init (DBusHeader *header);
@@ -108,7 +109,8 @@ dbus_bool_t _dbus_header_have_message_untrusted (int max_messag
int *body_len,
const DBusString *str,
int start,
- int len);
+ int len,
+ dbus_bool_t *is_gvariant);
dbus_bool_t _dbus_header_load (DBusHeader *header,
DBusValidationMode mode,
DBusValidity *validity,
diff --git a/dbus/dbus-marshal-recursive.c b/dbus/dbus-marshal-recursive.c
index 4adfd2e9..8a5c90e1 100644
--- a/dbus/dbus-marshal-recursive.c
+++ b/dbus/dbus-marshal-recursive.c
@@ -2,6 +2,7 @@
/* dbus-marshal-recursive.c Marshalling routines for recursive types
*
* Copyright (C) 2004, 2005 Red Hat, Inc.
+ * Copyright (C) 2015 Samsung Electronics
*
* Licensed under the Academic Free License version 2.1
*
@@ -26,6 +27,7 @@
#include "dbus-marshal-basic.h"
#include "dbus-signature.h"
#include "dbus-internals.h"
+#include "dbus-marshal-gvariant.h"
/**
* @addtogroup DBusMarshal
@@ -147,7 +149,8 @@ reader_init (DBusTypeReader *reader,
const DBusString *type_str,
int type_pos,
const DBusString *value_str,
- int value_pos)
+ int value_pos,
+ dbus_bool_t gvariant)
{
reader->byte_order = byte_order;
reader->finished = FALSE;
@@ -155,6 +158,11 @@ reader_init (DBusTypeReader *reader,
reader->type_pos = type_pos;
reader->value_str = value_str;
reader->value_pos = value_pos;
+ reader->value_start = value_pos;
+ reader->gvariant = gvariant;
+ reader->variable_index = 0;
+ reader->offsets_from_back = TRUE;
+ reader->is_variant = FALSE;
}
static void
@@ -167,7 +175,8 @@ base_reader_recurse (DBusTypeReader *sub,
parent->type_str,
parent->type_pos,
parent->value_str,
- parent->value_pos);
+ parent->value_pos,
+ parent->gvariant);
}
static void
@@ -190,8 +199,31 @@ struct_or_dict_entry_reader_recurse (DBusTypeReader *sub,
{
struct_or_dict_entry_types_only_reader_recurse (sub, parent);
- /* struct and dict entry have 8 byte alignment */
- sub->value_pos = _DBUS_ALIGN_VALUE (sub->value_pos, 8);
+ if (sub->gvariant)
+ {
+ /* GVARIANT */
+ /* check if current type is fixed or variable */
+ int alignment = 1;
+ int size = _dbus_reader_get_type_fixed_size (parent, &alignment);
+ sub->value_pos = _DBUS_ALIGN_VALUE (sub->value_pos, alignment); /* adjust alignment */
+ sub->value_start = sub->value_pos;
+ sub->n_offsets = _dbus_reader_count_offsets (sub);
+ sub->offsets_from_back = TRUE;
+
+ if (0 == size)
+ {
+ sub->value_end = _dbus_reader_get_offset_of_end_of_variable (parent);
+ }
+ else
+ {
+ sub->value_end = sub->value_pos + size;
+ }
+ }
+ else
+ {
+ /* struct and dict entry have 8 byte alignment */
+ sub->value_pos = sub->value_start = _DBUS_ALIGN_VALUE (sub->value_pos, 8);
+ }
}
static void
@@ -219,6 +251,9 @@ array_reader_get_array_len (const DBusTypeReader *reader)
dbus_uint32_t array_len;
int len_pos;
+ if (reader->gvariant)
+ return reader->value_end - reader->value_start;
+
len_pos = ARRAY_READER_LEN_POS (reader);
_dbus_assert (_DBUS_ALIGN_VALUE (len_pos, 4) == (unsigned) len_pos);
@@ -244,20 +279,38 @@ array_reader_recurse (DBusTypeReader *sub,
array_types_only_reader_recurse (sub, parent);
- sub->value_pos = _DBUS_ALIGN_VALUE (sub->value_pos, 4);
+ if (sub->gvariant)
+ {
+ int size = _dbus_reader_get_type_fixed_size (sub, &alignment);
+ sub->value_pos = _DBUS_ALIGN_VALUE (sub->value_pos, alignment);
+ sub->value_start = sub->value_pos;
+ sub->offsets_from_back = FALSE;
+ sub->value_end = _dbus_reader_get_offset_of_end_of_variable (parent);
+ sub->variable_index = 0;
+ if (0 == size)
+ sub->n_offsets = _dbus_reader_count_array_elems (sub);
+ else
+ sub->n_offsets = 0;
+ sub->u.array.start_pos = sub->value_start;
+ sub->finished = (sub->value_end == sub->value_start);
+ }
+ else
+ {
+ sub->value_pos = _DBUS_ALIGN_VALUE (sub->value_pos, 4);
- len_pos = sub->value_pos;
+ len_pos = sub->value_pos;
- sub->value_pos += 4; /* for the length */
+ sub->value_pos += 4; /* for the length */
- alignment = element_type_get_alignment (sub->type_str,
- sub->type_pos);
+ alignment = element_type_get_alignment (sub->type_str,
+ sub->type_pos);
- sub->value_pos = _DBUS_ALIGN_VALUE (sub->value_pos, alignment);
+ sub->value_pos = _DBUS_ALIGN_VALUE (sub->value_pos, alignment);
- sub->u.array.start_pos = sub->value_pos;
- _dbus_assert ((sub->u.array.start_pos - (len_pos + 4)) < 8); /* only 3 bits in array_len_offset */
- sub->array_len_offset = sub->u.array.start_pos - (len_pos + 4);
+ sub->u.array.start_pos = sub->value_pos;
+ _dbus_assert ((sub->u.array.start_pos - (len_pos + 4)) < 8); /* only 3 bits in array_len_offset */
+ sub->array_len_offset = sub->u.array.start_pos - (len_pos + 4);
+ }
#if RECURSIVE_MARSHAL_READ_TRACE
_dbus_verbose (" type reader %p array start = %d len_offset = %d array len = %d array element type = %s\n",
@@ -279,21 +332,46 @@ variant_reader_recurse (DBusTypeReader *sub,
base_reader_recurse (sub, parent);
- /* Variant is 1 byte sig length (without nul), signature with nul,
- * padding to 8-boundary, then values
- */
+ if (sub->gvariant)
+ {
+ /* GVariant's Variant is values, then nul byte, then signature.
+ * Variant's alignment is 8.
+ */
+ sub->value_pos = sub->value_start = _DBUS_ALIGN_VALUE (sub->value_pos, 8); /* adjust alignment */
+ sub->value_end = _dbus_reader_get_offset_of_end_of_variable (parent);
- sig_len = _dbus_string_get_byte (sub->value_str, sub->value_pos);
+ /* find beginning of signature in variant */
+ sub->type_str = sub->value_str;
+ sub->type_pos = sub->value_end - 1;
- sub->type_str = sub->value_str;
- sub->type_pos = sub->value_pos + 1;
+ while (sub->type_pos > 0 && _dbus_string_get_byte (sub->type_str, sub->type_pos) != 0)
+ sub->type_pos--;
- sub->value_pos = sub->type_pos + sig_len + 1;
+ if (_dbus_string_get_byte (sub->type_str, sub->type_pos) == 0)
+ sub->type_pos++;
- contained_alignment = _dbus_type_get_alignment (_dbus_first_type_in_signature (sub->type_str,
- sub->type_pos));
-
- sub->value_pos = _DBUS_ALIGN_VALUE (sub->value_pos, contained_alignment);
+ /* set the end of variant's value to the zero byte before signature */
+ sub->value_end = sub->type_pos - 1;
+ sub->is_variant = TRUE;
+ }
+ else
+ {
+ /* Variant is 1 byte sig length (without nul), signature with nul,
+ * padding to 8-boundary, then values
+ */
+
+ sig_len = _dbus_string_get_byte (sub->value_str, sub->value_pos);
+
+ sub->type_str = sub->value_str;
+ sub->type_pos = sub->value_pos + 1;
+
+ sub->value_pos = sub->type_pos + sig_len + 1;
+
+ contained_alignment = _dbus_type_get_alignment (_dbus_first_type_in_signature (sub->type_str,
+ sub->type_pos));
+
+ sub->value_pos = _DBUS_ALIGN_VALUE (sub->value_pos, contained_alignment);
+ }
#if RECURSIVE_MARSHAL_READ_TRACE
_dbus_verbose (" type reader %p variant containing '%s'\n",
@@ -338,7 +416,7 @@ skip_one_complete_type (const DBusString *type_str,
*/
void
_dbus_type_signature_next (const char *type_str,
- int *type_pos)
+ int *type_pos)
{
const unsigned char *p;
const unsigned char *start;
@@ -443,6 +521,7 @@ base_reader_next (DBusTypeReader *reader,
case DBUS_TYPE_STRUCT:
case DBUS_TYPE_VARIANT:
/* Scan forward over the entire container contents */
+ /* FIXME for GVariant - use offsets */
{
DBusTypeReader sub;
@@ -489,7 +568,8 @@ base_reader_next (DBusTypeReader *reader,
default:
if (!reader->klass->types_only)
- _dbus_marshal_skip_basic (reader->value_str,
+ (reader->gvariant ? _dbus_marshal_skip_gvariant_basic : _dbus_marshal_skip_basic) (
+ reader->value_str,
current_type, reader->byte_order,
&reader->value_pos);
@@ -499,12 +579,40 @@ base_reader_next (DBusTypeReader *reader,
}
static void
+struct_or_dict_entry_reader_next (DBusTypeReader *reader,
+ int current_type)
+{
+ if (reader->gvariant)
+ {
+ int alignment;
+ int size = _dbus_reader_get_type_fixed_size (reader, &alignment);
+ if (0 == size)
+ {
+ /* variable size - use offsets*/
+ reader->value_pos = _dbus_reader_get_offset_of_end_of_variable (reader);
+ reader->variable_index++;
+ }
+ else
+ {
+ /* just move, but consider alignment */
+ reader->value_pos = _DBUS_ALIGN_VALUE(reader->value_pos, alignment) + size;
+ }
+
+ skip_one_complete_type (reader->type_str, &reader->type_pos);
+ }
+ else
+ {
+ base_reader_next (reader, current_type);
+ }
+}
+
+static void
struct_reader_next (DBusTypeReader *reader,
int current_type)
{
int t;
- base_reader_next (reader, current_type);
+ struct_or_dict_entry_reader_next (reader, current_type);
/* for STRUCT containers we return FALSE at the end of the struct,
* for INVALID we return FALSE at the end of the signature.
@@ -520,12 +628,22 @@ struct_reader_next (DBusTypeReader *reader,
}
static void
+body_reader_next (DBusTypeReader *reader,
+ int current_type)
+{
+ if (reader->gvariant)
+ struct_reader_next (reader, current_type);
+ else
+ base_reader_next (reader, current_type);
+}
+
+static void
dict_entry_reader_next (DBusTypeReader *reader,
int current_type)
{
int t;
- base_reader_next (reader, current_type);
+ struct_or_dict_entry_reader_next (reader, current_type);
/* for STRUCT containers we return FALSE at the end of the struct,
* for INVALID we return FALSE at the end of the signature.
@@ -559,6 +677,26 @@ array_reader_next (DBusTypeReader *reader,
/* Skip one array element */
int end_pos;
+ if (reader->gvariant)
+ {
+ int alignment;
+ int size = _dbus_reader_get_type_fixed_size (reader, &alignment);
+ if (0 == size)
+ {
+ /* variable size - use offsets*/
+ reader->value_pos = _dbus_reader_get_offset_of_end_of_variable (reader);
+ reader->variable_index++;
+ reader->finished = (reader->variable_index >= reader->n_offsets);
+ }
+ else
+ {
+ /* fixed size - move on; consider alignment */
+ reader->value_pos = _DBUS_ALIGN_VALUE(reader->value_pos, alignment) + size;
+ reader->finished = (reader->value_pos >= reader->value_end);
+ }
+ return;
+ }
+
end_pos = reader->u.array.start_pos + array_reader_get_array_len (reader);
#if RECURSIVE_MARSHAL_READ_TRACE
@@ -631,12 +769,33 @@ array_reader_next (DBusTypeReader *reader,
}
}
+static void
+variant_reader_next (DBusTypeReader *reader,
+ int current_type)
+{
+ if (reader->gvariant)
+ {
+ if (!reader->klass->types_only)
+ reader->value_pos = reader->value_end;
+
+ reader->type_pos += 1;
+
+ reader->finished = TRUE;
+
+ reader->variable_index++;
+ }
+ else
+ {
+ base_reader_next (reader, current_type);
+ }
+}
+
static const DBusTypeReaderClass body_reader_class = {
"body", 0,
FALSE,
NULL, /* body is always toplevel, so doesn't get recursed into */
NULL,
- base_reader_next
+ body_reader_next
};
static const DBusTypeReaderClass body_types_only_reader_class = {
@@ -644,7 +803,7 @@ static const DBusTypeReaderClass body_types_only_reader_class = {
TRUE,
NULL, /* body is always toplevel, so doesn't get recursed into */
NULL,
- base_reader_next
+ body_reader_next
};
static const DBusTypeReaderClass struct_reader_class = {
@@ -700,7 +859,7 @@ static const DBusTypeReaderClass variant_reader_class = {
FALSE,
variant_reader_recurse,
NULL,
- base_reader_next
+ variant_reader_next
};
#ifndef DBUS_DISABLE_ASSERT
@@ -739,7 +898,7 @@ _dbus_type_reader_init (DBusTypeReader *reader,
reader->klass = &body_reader_class;
reader_init (reader, byte_order, type_str, type_pos,
- value_str, value_pos);
+ value_str, value_pos, FALSE);
#if RECURSIVE_MARSHAL_READ_TRACE
_dbus_verbose (" type reader %p init type_pos = %d value_pos = %d remaining sig '%s'\n",
@@ -764,7 +923,8 @@ _dbus_type_reader_init_types_only (DBusTypeReader *reader,
reader->klass = &body_types_only_reader_class;
reader_init (reader, DBUS_COMPILER_BYTE_ORDER /* irrelevant */,
- type_str, type_pos, NULL, _DBUS_INT_MAX /* crashes if we screw up */);
+ type_str, type_pos, NULL, _DBUS_INT_MAX /* crashes if we screw up */,
+ FALSE);
#if RECURSIVE_MARSHAL_READ_TRACE
_dbus_verbose (" type reader %p init types only type_pos = %d remaining sig '%s'\n",
@@ -875,7 +1035,8 @@ _dbus_type_reader_read_basic (const DBusTypeReader *reader,
t = _dbus_type_reader_get_current_type (reader);
- _dbus_marshal_read_basic (reader->value_str,
+ (reader->gvariant ? _dbus_marshal_read_gvariant_basic : _dbus_marshal_read_basic) (
+ reader->value_str,
reader->value_pos,
t, value,
reader->byte_order,
@@ -1499,9 +1660,14 @@ _dbus_type_writer_init (DBusTypeWriter *writer,
writer->type_pos = type_pos;
writer->value_str = value_str;
writer->value_pos = value_pos;
+ writer->value_start = value_pos;
writer->container_type = DBUS_TYPE_INVALID;
writer->type_pos_is_expectation = FALSE;
writer->enabled = TRUE;
+ writer->gvariant = FALSE;
+ writer->body_container = FALSE;
+ writer->is_fixed = TRUE;
+ writer->alignment = 1;
#if RECURSIVE_MARSHAL_WRITE_TRACE
_dbus_verbose ("writer %p init remaining sig '%s'\n", writer,
@@ -1531,6 +1697,24 @@ _dbus_type_writer_init_types_delayed (DBusTypeWriter *writer,
NULL, 0, value_str, value_pos);
}
+void
+_dbus_type_writer_gvariant_init_types_delayed (DBusTypeWriter *writer,
+ int byte_order,
+ DBusString *value_str,
+ int value_pos,
+ dbus_bool_t gvariant)
+{
+ _dbus_type_writer_init (writer, byte_order,
+ NULL, 0, value_str, value_pos);
+ writer->gvariant = gvariant;
+ writer->body_container = TRUE;
+ writer->is_fixed = TRUE;
+ writer->alignment = 8;
+ writer->u.struct_or_dict.last_offset = 0;
+ writer->offsets_size = 1;
+ writer->offsets = NULL;
+}
+
/**
* Adds type string to the writer, if it had none.
*
@@ -1598,12 +1782,19 @@ _dbus_type_writer_write_basic_no_typecode (DBusTypeWriter *writer,
const void *value)
{
if (writer->enabled)
- return _dbus_marshal_write_basic (writer->value_str,
- writer->value_pos,
- type,
- value,
- writer->byte_order,
- &writer->value_pos);
+ {
+ if (writer->gvariant)
+ {
+ return _dbus_type_writer_gvariant_write_basic_no_typecode (writer, type, value);
+ }
+ else
+ return _dbus_marshal_write_basic (writer->value_str,
+ writer->value_pos,
+ type,
+ value,
+ writer->byte_order,
+ &writer->value_pos);
+ }
else
return TRUE;
}
@@ -1642,6 +1833,7 @@ writer_recurse_init_and_check (DBusTypeWriter *writer,
writer->value_pos);
sub->container_type = container_type;
+ sub->gvariant = writer->gvariant;
if (writer->type_pos_is_expectation ||
(sub->container_type == DBUS_TYPE_ARRAY || sub->container_type == DBUS_TYPE_VARIANT))
@@ -1792,12 +1984,28 @@ writer_recurse_struct_or_dict_entry (DBusTypeWriter *writer,
if (writer->enabled)
{
- if (!_dbus_string_insert_bytes (sub->value_str,
- sub->value_pos,
- _DBUS_ALIGN_VALUE (sub->value_pos, 8) - sub->value_pos,
- '\0'))
- _dbus_assert_not_reached ("should not have failed to insert alignment padding for struct");
- sub->value_pos = _DBUS_ALIGN_VALUE (sub->value_pos, 8);
+ if (writer->gvariant)
+ {
+ sub->alignment = 1;
+ sub->value_str = dbus_new (DBusString, 1);
+ if (NULL == sub->value_str || !_dbus_string_init (sub->value_str))
+ return FALSE;
+ sub->value_start = sub->value_pos = 0;
+ sub->u.struct_or_dict.last_offset = 0;
+ sub->offsets_size = 1;
+ sub->is_fixed = TRUE;
+ sub->offsets = dbus_new (DBusString, 1);
+ _dbus_string_init (sub->offsets);
+ }
+ else
+ {
+ if (!_dbus_string_insert_bytes (sub->value_str,
+ sub->value_pos,
+ _DBUS_ALIGN_VALUE (sub->value_pos, 8) - sub->value_pos,
+ '\0'))
+ _dbus_assert_not_reached ("should not have failed to insert alignment padding for struct");
+ sub->value_pos = _DBUS_ALIGN_VALUE (sub->value_pos, 8);
+ }
}
return TRUE;
@@ -1885,27 +2093,47 @@ writer_recurse_array (DBusTypeWriter *writer,
if (writer->enabled)
{
- /* Write (or jump over, if is_array_append) the length */
- sub->u.array.len_pos = _DBUS_ALIGN_VALUE (sub->value_pos, 4);
-
- if (is_array_append)
- {
- sub->value_pos += 4;
- }
- else
+ if (!writer->gvariant)
{
- if (!_dbus_type_writer_write_basic_no_typecode (sub, DBUS_TYPE_UINT32,
- &value))
- _dbus_assert_not_reached ("should not have failed to insert array len");
- }
+ /* Write (or jump over, if is_array_append) the length */
+ sub->u.array.len_pos = _DBUS_ALIGN_VALUE (sub->value_pos, 4);
+
+ if (is_array_append)
+ {
+ sub->value_pos += 4;
+ }
+ else
+ {
+ if (!_dbus_type_writer_write_basic_no_typecode (sub, DBUS_TYPE_UINT32, &value))
+ _dbus_assert_not_reached ("should not have failed to insert array len");
+ }
- _dbus_assert (sub->u.array.len_pos == sub->value_pos - 4);
+ _dbus_assert (sub->u.array.len_pos == sub->value_pos - 4);
+ }
/* Write alignment padding for array elements
* Note that we write the padding *even for empty arrays*
* to avoid wonky special cases
*/
- alignment = element_type_get_alignment (contained_type, contained_type_start);
+ if (writer->gvariant)
+ {
+ int size = _dbus_type_gvariant_get_fixed_size (contained_type, contained_type_start, &alignment);
+ if (0 == size)
+ {
+ sub->offsets_size = 1;
+ sub->offsets = dbus_new (DBusString, 1);
+ _dbus_string_init (sub->offsets);
+ }
+ else
+ {
+ sub->offsets_size = 0;
+ sub->offsets = NULL;
+ }
+ }
+ else
+ {
+ alignment = element_type_get_alignment (contained_type, contained_type_start);
+ }
aligned = _DBUS_ALIGN_VALUE (sub->value_pos, alignment);
if (aligned != sub->value_pos)
@@ -1924,7 +2152,7 @@ writer_recurse_array (DBusTypeWriter *writer,
sub->u.array.start_pos = sub->value_pos;
- if (is_array_append)
+ if (is_array_append && !writer->gvariant)
{
dbus_uint32_t len;
@@ -1937,6 +2165,12 @@ writer_recurse_array (DBusTypeWriter *writer,
sub->value_pos += len;
}
+ if (writer->gvariant)
+ {
+ sub->alignment = alignment;
+ sub->is_fixed = FALSE;
+ sub->value_start = sub->value_pos;
+ }
}
else
{
@@ -1945,7 +2179,7 @@ writer_recurse_array (DBusTypeWriter *writer,
sub->u.array.start_pos = sub->value_pos;
}
- _dbus_assert (sub->u.array.len_pos < sub->u.array.start_pos);
+ _dbus_assert (sub->gvariant || sub->u.array.len_pos < sub->u.array.start_pos);
_dbus_assert (is_array_append || sub->u.array.start_pos == sub->value_pos);
#if RECURSIVE_MARSHAL_WRITE_TRACE
@@ -2012,31 +2246,58 @@ writer_recurse_variant (DBusTypeWriter *writer,
/* If we're enabled then continue ... */
- if (!_dbus_string_insert_byte (sub->value_str,
- sub->value_pos,
- contained_type_len))
- _dbus_assert_not_reached ("should not have failed to insert variant type sig len");
+ if (writer->gvariant)
+ {
+ /* GVariant case:
+ * contents, then nul byte, then signature without nul byte.
+ * The alignment is always 8.
+ *
+ * Signature is at the end of a variant. So, the easiest way is to write it down
+ * when unrecursing. So, we need to copy it to a new string.
+ */
+ contained_alignment = 8;
+ sub->alignment = 8;
+ sub->type_str = dbus_new (DBusString, 1); /* to be deallocated on unrecurse */
+ sub->type_pos = 0;
+ sub->is_fixed = FALSE;
+ _dbus_string_init_preallocated (sub->type_str, contained_type_len);
+
+ if (!_dbus_string_copy_len (contained_type, contained_type_start, contained_type_len,
+ sub->type_str, sub->type_pos))
+ _dbus_assert_not_reached ("should not have failed to insert variant type sig");
+ }
+ else
+ {
+ /* dbus1 case:
+ * length, signature with nul byte, then contents
+ * alignment depends on contents.
+ */
+ if (!_dbus_string_insert_byte (sub->value_str,
+ sub->value_pos,
+ contained_type_len))
+ _dbus_assert_not_reached ("should not have failed to insert variant type sig len");
- sub->value_pos += 1;
+ sub->value_pos += 1;
- /* Here we switch over to the expected type sig we're about to write */
- sub->type_str = sub->value_str;
- sub->type_pos = sub->value_pos;
+ /* Here we switch over to the expected type sig we're about to write */
+ sub->type_str = sub->value_str;
+ sub->type_pos = sub->value_pos;
- if (!_dbus_string_copy_len (contained_type, contained_type_start, contained_type_len,
- sub->value_str, sub->value_pos))
- _dbus_assert_not_reached ("should not have failed to insert variant type sig");
+ if (!_dbus_string_copy_len (contained_type, contained_type_start, contained_type_len,
+ sub->value_str, sub->value_pos))
+ _dbus_assert_not_reached ("should not have failed to insert variant type sig");
- sub->value_pos += contained_type_len;
+ sub->value_pos += contained_type_len;
- if (!_dbus_string_insert_byte (sub->value_str,
- sub->value_pos,
- DBUS_TYPE_INVALID))
- _dbus_assert_not_reached ("should not have failed to insert variant type nul termination");
+ if (!_dbus_string_insert_byte (sub->value_str,
+ sub->value_pos,
+ DBUS_TYPE_INVALID))
+ _dbus_assert_not_reached ("should not have failed to insert variant type nul termination");
- sub->value_pos += 1;
+ sub->value_pos += 1;
- contained_alignment = _dbus_type_get_alignment (_dbus_first_type_in_signature (contained_type, contained_type_start));
+ contained_alignment = _dbus_type_get_alignment (_dbus_first_type_in_signature (contained_type, contained_type_start));
+ }
if (!_dbus_string_insert_bytes (sub->value_str,
sub->value_pos,
@@ -2044,6 +2305,7 @@ writer_recurse_variant (DBusTypeWriter *writer,
'\0'))
_dbus_assert_not_reached ("should not have failed to insert alignment padding for variant body");
sub->value_pos = _DBUS_ALIGN_VALUE (sub->value_pos, contained_alignment);
+ sub->value_start = sub->value_pos;
return TRUE;
}
@@ -2116,6 +2378,8 @@ _dbus_type_writer_recurse (DBusTypeWriter *writer,
else
contained_type_len = 0;
+ sub->body_container = FALSE;
+
return _dbus_type_writer_recurse_contained_len (writer, container_type,
contained_type,
contained_type_start,
@@ -2164,32 +2428,10 @@ writer_get_array_len (DBusTypeWriter *writer)
return writer->value_pos - writer->u.array.start_pos;
}
-/**
- * Closes a container created by _dbus_type_writer_recurse()
- * and writes any additional information to the values block.
- *
- * @param writer the writer
- * @param sub the sub-writer created by _dbus_type_writer_recurse()
- * @returns #FALSE if no memory
- */
-dbus_bool_t
-_dbus_type_writer_unrecurse (DBusTypeWriter *writer,
- DBusTypeWriter *sub)
+static dbus_bool_t
+_dbus_type_writer_unrecurse_write (DBusTypeWriter *writer,
+ DBusTypeWriter *sub)
{
- /* type_pos_is_expectation never gets unset once set, or we'd get all hosed */
- _dbus_assert (!writer->type_pos_is_expectation ||
- (writer->type_pos_is_expectation && sub->type_pos_is_expectation));
-
-#if RECURSIVE_MARSHAL_WRITE_TRACE
- _dbus_verbose (" type writer %p unrecurse type_pos = %d value_pos = %d is_expectation = %d container_type = %s\n",
- writer, writer->type_pos, writer->value_pos, writer->type_pos_is_expectation,
- _dbus_type_to_string (writer->container_type));
- _dbus_verbose (" type writer %p unrecurse sub type_pos = %d value_pos = %d is_expectation = %d container_type = %s\n",
- sub, sub->type_pos, sub->value_pos,
- sub->type_pos_is_expectation,
- _dbus_type_to_string (sub->container_type));
-#endif
-
if (sub->container_type == DBUS_TYPE_STRUCT)
{
if (!write_or_verify_typecode (sub, DBUS_STRUCT_END_CHAR))
@@ -2200,7 +2442,7 @@ _dbus_type_writer_unrecurse (DBusTypeWriter *writer,
if (!write_or_verify_typecode (sub, DBUS_DICT_ENTRY_END_CHAR))
return FALSE;
}
- else if (sub->container_type == DBUS_TYPE_ARRAY)
+ else if (sub->container_type == DBUS_TYPE_ARRAY && !sub->gvariant)
{
if (sub->u.array.len_pos >= 0) /* len_pos == -1 if we weren't enabled when we passed it */
{
@@ -2224,6 +2466,46 @@ _dbus_type_writer_unrecurse (DBusTypeWriter *writer,
}
#endif
}
+ return TRUE;
+}
+
+/**
+ * Closes a container created by _dbus_type_writer_recurse()
+ * and writes any additional information to the values block.
+ *
+ * @param writer the writer
+ * @param sub the sub-writer created by _dbus_type_writer_recurse()
+ * @returns #FALSE if no memory
+ */
+dbus_bool_t
+_dbus_type_writer_unrecurse (DBusTypeWriter *writer,
+ DBusTypeWriter *sub)
+{
+ /* type_pos_is_expectation never gets unset once set, or we'd get all hosed */
+ _dbus_assert (!writer->type_pos_is_expectation ||
+ (writer->type_pos_is_expectation && sub->type_pos_is_expectation));
+
+#if RECURSIVE_MARSHAL_WRITE_TRACE
+ _dbus_verbose (" type writer %p unrecurse type_pos = %d value_pos = %d is_expectation = %d container_type = %s\n",
+ writer, writer->type_pos, writer->value_pos, writer->type_pos_is_expectation,
+ _dbus_type_to_string (writer->container_type));
+ _dbus_verbose (" type writer %p unrecurse sub type_pos = %d value_pos = %d is_expectation = %d container_type = %s\n",
+ sub, sub->type_pos, sub->value_pos,
+ sub->type_pos_is_expectation,
+ _dbus_type_to_string (sub->container_type));
+#endif
+
+ if (!_dbus_type_writer_unrecurse_write (writer, sub))
+ return FALSE;
+
+ if (writer->gvariant)
+ {
+ if (!_dbus_writer_unrecurse_gvariant_write (writer, sub))
+ return FALSE;
+ }
+ else
+ writer->value_pos = sub->value_pos;
+
/* Now get type_pos right for the parent writer. Here are the cases:
*
@@ -2283,8 +2565,6 @@ _dbus_type_writer_unrecurse (DBusTypeWriter *writer,
}
}
- writer->value_pos = sub->value_pos;
-
#if RECURSIVE_MARSHAL_WRITE_TRACE
_dbus_verbose (" type writer %p unrecursed type_pos = %d value_pos = %d remaining sig '%s'\n",
writer, writer->type_pos, writer->value_pos,
diff --git a/dbus/dbus-marshal-recursive.h b/dbus/dbus-marshal-recursive.h
index acbfd731..0873c5b8 100644
--- a/dbus/dbus-marshal-recursive.h
+++ b/dbus/dbus-marshal-recursive.h
@@ -2,6 +2,7 @@
/* dbus-marshal-recursive.h Marshalling routines for recursive types
*
* Copyright (C) 2004, 2005 Red Hat, Inc.
+ * Copyright (C) 2015 Samsung Electronics
*
* Licensed under the Academic Free License version 2.1
*
@@ -44,10 +45,17 @@ struct DBusTypeReader
* where we don't have another way to tell
*/
dbus_uint32_t array_len_offset : 3; /**< bytes back from start_pos that len ends */
+ dbus_uint32_t gvariant : 1; /**< TRUE if gvariant marshaling should be used */
+ dbus_uint32_t offsets_from_back : 1; /**< for GVariant marshalling: direction of offsets */
+ dbus_uint32_t is_variant : 1; /**< for GVariant marshalling: indicator of variant type */
const DBusString *type_str; /**< string containing signature of block */
int type_pos; /**< current position in signature */
const DBusString *value_str; /**< string containing values of block */
int value_pos; /**< current position in values */
+ int variable_index; /**< index of value within variable values in the container */
+ size_t value_start; /**< start of container */
+ size_t value_end; /**< end of container */
+ int n_offsets; /**< for GVariant marshalling: number of variable offsets */
const DBusTypeReaderClass *klass; /**< the vtable for the reader */
union
@@ -71,10 +79,18 @@ struct DBusTypeWriter
dbus_uint32_t enabled : 1; /**< whether to write values */
+ dbus_uint32_t gvariant : 1; /**< TRUE if gvariant marshaling should be used */
+ dbus_uint32_t body_container : 1; /**< TRUE if this writer is top-level */
+ dbus_uint32_t is_fixed : 1; /**< TRUE if this writer wrote only fixed-size values so far */
+
DBusString *type_str; /**< where to write typecodes (or read type expectations) */
int type_pos; /**< current pos in type_str */
DBusString *value_str; /**< where to write values */
int value_pos; /**< next position to write */
+ size_t value_start; /**< start of the container */
+ DBusString *offsets; /**< for GVariant marshalling: actual offsets */
+ int alignment; /**< for GVariant marshalling: for enclosing containers */
+ char offsets_size; /**< for GVariant marshalling: current size of offsets */
union
{
@@ -83,6 +99,9 @@ struct DBusTypeWriter
int len_pos; /**< position of length of the array */
int element_type_pos; /**< position of array element type in type_str */
} array;
+ struct {
+ size_t last_offset; /**< for GVariant marshalling: position of end of last field */
+ } struct_or_dict;
} u; /**< class-specific data */
};
@@ -146,6 +165,11 @@ void _dbus_type_writer_init_types_delayed (DBusTypeWriter *write
int byte_order,
DBusString *value_str,
int value_pos);
+void _dbus_type_writer_gvariant_init_types_delayed (DBusTypeWriter *writer,
+ int byte_order,
+ DBusString *value_str,
+ int value_pos,
+ dbus_bool_t gvariant);
void _dbus_type_writer_add_types (DBusTypeWriter *writer,
DBusString *type_str,
int type_pos);
@@ -159,6 +183,11 @@ void _dbus_type_writer_init_values_only (DBusTypeWriter *write
dbus_bool_t _dbus_type_writer_write_basic (DBusTypeWriter *writer,
int type,
const void *value);
+dbus_bool_t _dbus_type_writer_write_basic_with_gvariant
+ (DBusTypeWriter *writer,
+ int type,
+ const void *value,
+ dbus_bool_t gvariant);
dbus_bool_t _dbus_type_writer_write_fixed_multi (DBusTypeWriter *writer,
int element_type,
const void *value,
diff --git a/dbus/dbus-message-internal.h b/dbus/dbus-message-internal.h
index 5d6594e3..c4530e60 100644
--- a/dbus/dbus-message-internal.h
+++ b/dbus/dbus-message-internal.h
@@ -96,11 +96,23 @@ long _dbus_message_loader_get_max_message_size (DBusMessageLoader
void _dbus_message_loader_set_max_message_unix_fds(DBusMessageLoader *loader,
long n);
long _dbus_message_loader_get_max_message_unix_fds(DBusMessageLoader *loader);
+void _dbus_message_loader_set_unique_sender_id (DBusMessageLoader *loader,
+ uint64_t id);
+uint64_t _dbus_message_loader_get_unique_sender_id (DBusMessageLoader *loader);
typedef struct DBusInitialFDs DBusInitialFDs;
DBusInitialFDs *_dbus_check_fdleaks_enter (void);
void _dbus_check_fdleaks_leave (DBusInitialFDs *fds);
+DBusMessage * _dbus_message_remarshal(DBusMessage *message, dbus_bool_t gvariant);
+
+DBusMessage * _dbus_generate_local_error_message (dbus_uint32_t serial,
+ char *error_name,
+ char *error_msg);
+
+dbus_bool_t _dbus_message_assure_dbus1 (DBusMessage **message);
+dbus_bool_t _dbus_message_assure_gvariant (DBusMessage **message);
+
DBUS_END_DECLS
#endif /* DBUS_MESSAGE_INTERNAL_H */
diff --git a/dbus/dbus-message-private.h b/dbus/dbus-message-private.h
index e1578abd..674716ae 100644
--- a/dbus/dbus-message-private.h
+++ b/dbus/dbus-message-private.h
@@ -81,6 +81,7 @@ struct DBusMessageLoader
unsigned n_unix_fds_allocated; /**< Number of file descriptors this array has space for */
unsigned n_unix_fds; /**< Number of valid file descriptors in array */
#endif
+ uint64_t unique_sender_id;
};
@@ -129,6 +130,8 @@ struct DBusMessage
long unix_fd_counter_delta; /**< Size we incremented the unix fd counter by */
#endif
+ DBusString *signature; /**< A placeholder for signature of received GVariant messages */
+ DBusString *unique_sender; /**< A placeholder for sender name of received GVariant messages */
};
dbus_bool_t _dbus_message_iter_get_args_valist (DBusMessageIter *iter,
diff --git a/dbus/dbus-message.c b/dbus/dbus-message.c
index c6953d02..4952c17c 100644
--- a/dbus/dbus-message.c
+++ b/dbus/dbus-message.c
@@ -3,6 +3,7 @@
*
* Copyright (C) 2002, 2003, 2004, 2005 Red Hat Inc.
* Copyright (C) 2002, 2003 CodeFactory AB
+ * Copyright (C) 2015 Samsung Electronics
*
* Licensed under the Academic Free License version 2.1
*
@@ -37,6 +38,8 @@
#ifdef HAVE_UNIX_FD_PASSING
#include "dbus-sysdeps-unix.h"
#endif
+#include "dbus-marshal-gvariant.h"
+#include "dbus-protocol-gvariant.h"
#include <string.h>
@@ -44,6 +47,8 @@
(type == DBUS_TYPE_STRING || type == DBUS_TYPE_SIGNATURE || \
type == DBUS_TYPE_OBJECT_PATH)
+unsigned char _dbus_default_protocol_version = DBUS_PROTOCOL_VERSION_GVARIANT;
+
static void dbus_message_finalize (DBusMessage *message);
/**
@@ -117,6 +122,8 @@ enum {
/** typedef for internals of message iterator */
typedef struct DBusMessageRealIter DBusMessageRealIter;
+#define BUILD_BUG_ON(condition) ((void)sizeof(char[1 - 2*!!(condition)]))
+
/**
* @brief Internals of DBusMessageIter
*
@@ -135,19 +142,80 @@ struct DBusMessageRealIter
} u; /**< the type writer or reader that does all the work */
};
+static dbus_bool_t
+_dbus_header_is_gvariant (const DBusHeader *header)
+{
+ return (header->protocol_version == DBUS_PROTOCOL_VERSION_GVARIANT);
+}
+
+static dbus_bool_t
+_dbus_message_is_gvariant (const DBusMessage *message)
+{
+ return _dbus_header_is_gvariant (&message->header);
+}
+
+static void
+_dbus_message_toggle_gvariant (DBusMessage *message, dbus_bool_t gvariant)
+{
+ message->header.protocol_version = gvariant ? DBUS_PROTOCOL_VERSION_GVARIANT : DBUS_MAJOR_PROTOCOL_VERSION;
+}
+
static void
-get_const_signature (DBusHeader *header,
+get_const_signature (DBusMessage *message,
const DBusString **type_str_p,
int *type_pos_p)
{
- if (_dbus_header_get_field_raw (header,
- DBUS_HEADER_FIELD_SIGNATURE,
- type_str_p,
- type_pos_p))
+ dbus_bool_t got_signature = FALSE;
+ if (_dbus_message_is_gvariant (message) && message->locked)
{
- *type_pos_p += 1; /* skip the signature length which is 1 byte */
+ /* only locked GVariant messages have signatures in the body */
+ /*
+ * in case of received GVariant message, there may be no signature field in a header,
+ * but in the body. However, it is not nul-terminated.
+ * So, we need to allocate space and put it into message.
+ * It could also happen before, so check message->signature for already existing.
+ * FIXME: That may kinda break oom-safety.
+ * For now - if oom, then return empty signature.
+ */
+ if (message->signature == NULL)
+ {
+ int type_str_len;
+ got_signature = _dbus_message_gvariant_get_signature (message,
+ type_str_p,
+ type_pos_p,
+ &type_str_len);
+ if (got_signature)
+ {
+ message->signature = dbus_new (DBusString, 1);
+ got_signature = got_signature &&
+ _dbus_string_init_preallocated (message->signature, type_str_len + 1);
+
+ got_signature = got_signature &&
+ _dbus_string_copy_len (*type_str_p, *type_pos_p, type_str_len,
+ message->signature, 0);
+ got_signature = got_signature &&
+ _dbus_string_append_byte (message->signature, 0);
+ }
+ }
+ else
+ got_signature = TRUE;
+
+ if (got_signature)
+ {
+ *type_str_p = message->signature;
+ *type_pos_p = 0;
+ }
}
- else
+ else if (_dbus_header_get_field_raw (&message->header,
+ DBUS_HEADER_FIELD_SIGNATURE,
+ type_str_p,
+ type_pos_p))
+ {
+ if (!_dbus_message_is_gvariant (message))
+ *type_pos_p += 1; /* skip the signature length which is 1 byte */
+ got_signature = TRUE;
+ }
+ if (!got_signature)
{
*type_str_p = &_dbus_empty_signature_str;
*type_pos_p = 0;
@@ -173,7 +241,7 @@ _dbus_message_byteswap (DBusMessage *message)
_dbus_verbose ("Swapping message into compiler byte order\n");
- get_const_signature (&message->header, &type_str, &type_pos);
+ get_const_signature (message, &type_str, &type_pos);
_dbus_marshal_byteswap (type_str, type_pos,
byte_order,
@@ -384,8 +452,11 @@ dbus_message_lock (DBusMessage *message)
{
if (!message->locked)
{
- _dbus_header_update_lengths (&message->header,
- _dbus_string_get_length (&message->body));
+ if (!_dbus_message_is_gvariant (message))
+ _dbus_header_update_lengths (&message->header,
+ _dbus_string_get_length (&message->body));
+ else
+ _dbus_message_finalize_gvariant (message, TRUE);
/* must have a signature if you have a body */
_dbus_assert (_dbus_string_get_length (&message->body) == 0 ||
@@ -665,6 +736,18 @@ dbus_message_cache_or_finalize (DBusMessage *message)
close_unix_fds(message->unix_fds, &message->n_unix_fds);
#endif
+ if (NULL != message->signature)
+ {
+ _dbus_string_free (message->signature);
+ message->signature = NULL;
+ }
+
+ if (NULL != message->unique_sender)
+ {
+ _dbus_string_free (message->unique_sender);
+ message->unique_sender = NULL;
+ }
+
was_cached = FALSE;
if (!_DBUS_LOCK (message_cache))
@@ -1142,13 +1225,25 @@ dbus_bool_t
dbus_message_set_reply_serial (DBusMessage *message,
dbus_uint32_t reply_serial)
{
+ int type = DBUS_TYPE_UINT32;
+
_dbus_return_val_if_fail (message != NULL, FALSE);
_dbus_return_val_if_fail (!message->locked, FALSE);
_dbus_return_val_if_fail (reply_serial != 0, FALSE); /* 0 is invalid */
+ if (_dbus_message_is_gvariant (message))
+ {
+ dbus_uint64_t reply_serial_uint64 = reply_serial;
+ type = DBUS_TYPE_UINT64;
+ return _dbus_header_set_field_basic (&message->header,
+ DBUS_HEADER_FIELD_REPLY_SERIAL,
+ type,
+ &reply_serial_uint64);
+ }
+
return _dbus_header_set_field_basic (&message->header,
DBUS_HEADER_FIELD_REPLY_SERIAL,
- DBUS_TYPE_UINT32,
+ type,
&reply_serial);
}
@@ -1162,14 +1257,28 @@ dbus_uint32_t
dbus_message_get_reply_serial (DBusMessage *message)
{
dbus_uint32_t v_UINT32;
+ dbus_uint64_t v_UINT64;
+ int type = DBUS_TYPE_UINT32;
+ void *value = &v_UINT32;
_dbus_return_val_if_fail (message != NULL, 0);
+ if (_dbus_message_is_gvariant (message))
+ {
+ type = DBUS_TYPE_UINT64;
+ value = &v_UINT64;
+ }
+
if (_dbus_header_get_field_basic (&message->header,
DBUS_HEADER_FIELD_REPLY_SERIAL,
- DBUS_TYPE_UINT32,
- &v_UINT32))
- return v_UINT32;
+ type,
+ value))
+ {
+ if (_dbus_message_is_gvariant (message))
+ return v_UINT64;
+ else
+ return v_UINT32;
+ }
else
return 0;
}
@@ -1200,7 +1309,7 @@ dbus_message_finalize (DBusMessage *message)
}
static DBusMessage*
-dbus_message_new_empty_header (void)
+dbus_message_new_empty_header (dbus_bool_t gvariant)
{
DBusMessage *message;
dbus_bool_t from_cache;
@@ -1245,6 +1354,8 @@ dbus_message_new_empty_header (void)
message->unix_fd_counter_delta = 0;
#endif
+ _dbus_message_toggle_gvariant (message, gvariant); /* this works only if kdbus is enabled */
+
if (!from_cache)
_dbus_data_slot_list_init (&message->slot_list);
@@ -1269,36 +1380,33 @@ dbus_message_new_empty_header (void)
}
}
+ message->signature = NULL;
+ message->unique_sender = NULL;
+
return message;
}
-/**
- * Constructs a new message of the given message type.
- * Types include #DBUS_MESSAGE_TYPE_METHOD_CALL,
- * #DBUS_MESSAGE_TYPE_SIGNAL, and so forth.
- *
- * Usually you want to use dbus_message_new_method_call(),
- * dbus_message_new_method_return(), dbus_message_new_signal(),
- * or dbus_message_new_error() instead.
- *
- * @param message_type type of message
- * @returns new message or #NULL if no memory
- */
-DBusMessage*
-dbus_message_new (int message_type)
+static DBusMessage*
+_dbus_message_create_protocol_version (int message_type,
+ const char *destination,
+ const char *path,
+ const char *interface,
+ const char *member,
+ const char *error_name,
+ dbus_bool_t gvariant)
{
DBusMessage *message;
- _dbus_return_val_if_fail (message_type != DBUS_MESSAGE_TYPE_INVALID, NULL);
+ _dbus_assert (message_type != DBUS_MESSAGE_TYPE_INVALID);
- message = dbus_message_new_empty_header ();
+ message = dbus_message_new_empty_header (gvariant);
if (message == NULL)
return NULL;
- if (!_dbus_header_create (&message->header,
+ if (!(_dbus_message_is_gvariant(message) ? _dbus_header_gvariant_create : _dbus_header_create) (&message->header,
DBUS_COMPILER_BYTE_ORDER,
message_type,
- NULL, NULL, NULL, NULL, NULL))
+ destination, path, interface, member, error_name))
{
dbus_message_unref (message);
return NULL;
@@ -1307,6 +1415,42 @@ dbus_message_new (int message_type)
return message;
}
+static DBusMessage*
+_dbus_message_create (int message_type,
+ const char *destination,
+ const char *path,
+ const char *interface,
+ const char *member,
+ const char *error_name)
+{
+ return _dbus_message_create_protocol_version(message_type,
+ destination,
+ path,
+ interface,
+ member,
+ error_name,
+ _dbus_default_protocol_version == DBUS_PROTOCOL_VERSION_GVARIANT);
+}
+
+/**
+ * Constructs a new message of the given message type.
+ * Types include #DBUS_MESSAGE_TYPE_METHOD_CALL,
+ * #DBUS_MESSAGE_TYPE_SIGNAL, and so forth.
+ *
+ * Usually you want to use dbus_message_new_method_call(),
+ * dbus_message_new_method_return(), dbus_message_new_signal(),
+ * or dbus_message_new_error() instead.
+ *
+ * @param message_type type of message
+ * @returns new message or #NULL if no memory
+ */
+DBusMessage*
+dbus_message_new (int message_type)
+{
+ return _dbus_message_create(message_type,
+ NULL, NULL, NULL, NULL, NULL);
+}
+
/**
* Constructs a new message to invoke a method on a remote
* object. Returns #NULL if memory can't be allocated for the
@@ -1334,8 +1478,6 @@ dbus_message_new_method_call (const char *destination,
const char *iface,
const char *method)
{
- DBusMessage *message;
-
_dbus_return_val_if_fail (path != NULL, NULL);
_dbus_return_val_if_fail (method != NULL, NULL);
_dbus_return_val_if_fail (destination == NULL ||
@@ -1345,20 +1487,8 @@ dbus_message_new_method_call (const char *destination,
_dbus_check_is_valid_interface (iface), NULL);
_dbus_return_val_if_fail (_dbus_check_is_valid_member (method), NULL);
- message = dbus_message_new_empty_header ();
- if (message == NULL)
- return NULL;
-
- if (!_dbus_header_create (&message->header,
- DBUS_COMPILER_BYTE_ORDER,
- DBUS_MESSAGE_TYPE_METHOD_CALL,
- destination, path, iface, method, NULL))
- {
- dbus_message_unref (message);
- return NULL;
- }
-
- return message;
+ return _dbus_message_create(DBUS_MESSAGE_TYPE_METHOD_CALL,
+ destination, path, iface, method, NULL);
}
/**
@@ -1380,19 +1510,11 @@ dbus_message_new_method_return (DBusMessage *method_call)
/* sender is allowed to be null here in peer-to-peer case */
- message = dbus_message_new_empty_header ();
+ message = _dbus_message_create (DBUS_MESSAGE_TYPE_METHOD_RETURN,
+ sender, NULL, NULL, NULL, NULL);
if (message == NULL)
return NULL;
- if (!_dbus_header_create (&message->header,
- DBUS_COMPILER_BYTE_ORDER,
- DBUS_MESSAGE_TYPE_METHOD_RETURN,
- sender, NULL, NULL, NULL, NULL))
- {
- dbus_message_unref (message);
- return NULL;
- }
-
dbus_message_set_no_reply (message, TRUE);
if (!dbus_message_set_reply_serial (message,
@@ -1433,19 +1555,11 @@ dbus_message_new_signal (const char *path,
_dbus_return_val_if_fail (_dbus_check_is_valid_interface (iface), NULL);
_dbus_return_val_if_fail (_dbus_check_is_valid_member (name), NULL);
- message = dbus_message_new_empty_header ();
+ message = _dbus_message_create (DBUS_MESSAGE_TYPE_SIGNAL,
+ NULL, path, iface, name, NULL);
if (message == NULL)
return NULL;
- if (!_dbus_header_create (&message->header,
- DBUS_COMPILER_BYTE_ORDER,
- DBUS_MESSAGE_TYPE_SIGNAL,
- NULL, path, iface, name, NULL))
- {
- dbus_message_unref (message);
- return NULL;
- }
-
dbus_message_set_no_reply (message, TRUE);
return message;
@@ -1484,19 +1598,11 @@ dbus_message_new_error (DBusMessage *reply_to,
* when the message bus is dealing with an unregistered
* connection.
*/
- message = dbus_message_new_empty_header ();
+ message = _dbus_message_create (DBUS_MESSAGE_TYPE_ERROR,
+ sender, NULL, NULL, NULL, error_name);
if (message == NULL)
return NULL;
- if (!_dbus_header_create (&message->header,
- DBUS_COMPILER_BYTE_ORDER,
- DBUS_MESSAGE_TYPE_ERROR,
- sender, NULL, NULL, NULL, error_name))
- {
- dbus_message_unref (message);
- return NULL;
- }
-
dbus_message_set_no_reply (message, TRUE);
if (!dbus_message_set_reply_serial (message,
@@ -1599,6 +1705,7 @@ dbus_message_copy (const DBusMessage *message)
#ifndef DBUS_DISABLE_CHECKS
retval->generation = message->generation;
#endif
+ _dbus_message_toggle_gvariant (retval, _dbus_message_is_gvariant (message));
if (!_dbus_header_copy (&message->header, &retval->header))
{
@@ -2067,19 +2174,24 @@ dbus_message_iter_init (DBusMessage *message,
const DBusString *type_str;
int type_pos;
+ BUILD_BUG_ON (sizeof(DBusMessageIter) != sizeof(DBusMessageRealIter));
+
_dbus_return_val_if_fail (message != NULL, FALSE);
_dbus_return_val_if_fail (iter != NULL, FALSE);
- get_const_signature (&message->header, &type_str, &type_pos);
+ get_const_signature (message, &type_str, &type_pos);
_dbus_message_iter_init_common (message, real,
DBUS_MESSAGE_ITER_TYPE_READER);
_dbus_type_reader_init (&real->u.reader,
- _dbus_header_get_byte_order (&message->header),
- type_str, type_pos,
- &message->body,
- 0);
+ _dbus_header_get_byte_order (&message->header),
+ type_str, type_pos,
+ &message->body,
+ 0);
+
+ if (_dbus_message_is_gvariant (message))
+ _dbus_type_reader_gvariant_init (&real->u.reader, message);
return _dbus_type_reader_get_current_type (&real->u.reader) != DBUS_TYPE_INVALID;
}
@@ -2432,10 +2544,12 @@ dbus_message_iter_init_append (DBusMessage *message,
* when a value is actually appended. That means that init() never fails
* due to OOM.
*/
- _dbus_type_writer_init_types_delayed (&real->u.writer,
- _dbus_header_get_byte_order (&message->header),
- &message->body,
- _dbus_string_get_length (&message->body));
+ _dbus_type_writer_gvariant_init_types_delayed (
+ &real->u.writer,
+ _dbus_header_get_byte_order (&message->header),
+ &message->body,
+ _dbus_string_get_length (&message->body),
+ _dbus_message_is_gvariant (message));
}
/**
@@ -2474,11 +2588,21 @@ _dbus_message_iter_open_signature (DBusMessageRealIter *real)
if (current_sig)
{
int current_len;
+ int additional_size_for_len = 0;
- current_len = _dbus_string_get_byte (current_sig, current_sig_pos);
- current_sig_pos += 1; /* move on to sig data */
+ if (!real->u.writer.gvariant)
+ {
+ current_len = _dbus_string_get_byte (current_sig, current_sig_pos);
+ current_sig_pos += 1; /* move on to sig data */
+ additional_size_for_len = 4;
+ }
+ else
+ {
+ /* GVariant has no length field, simply string */
+ current_len = strlen (_dbus_string_get_const_data (current_sig) + current_sig_pos);
+ }
- if (!_dbus_string_init_preallocated (str, current_len + 4))
+ if (!_dbus_string_init_preallocated (str, current_len + additional_size_for_len))
{
dbus_free (str);
return FALSE;
@@ -3512,6 +3636,9 @@ dbus_message_get_sender (DBusMessage *message)
_dbus_return_val_if_fail (message != NULL, NULL);
+ if (NULL != message->unique_sender)
+ return _dbus_string_get_const_data (message->unique_sender);
+
v = NULL; /* in case field doesn't exist */
_dbus_header_get_field_basic (&message->header,
DBUS_HEADER_FIELD_SENDER,
@@ -3546,7 +3673,7 @@ dbus_message_get_signature (DBusMessage *message)
_dbus_return_val_if_fail (message != NULL, NULL);
- get_const_signature (&message->header, &type_str, &type_pos);
+ get_const_signature (message, &type_str, &type_pos);
return _dbus_string_get_const_data_len (type_str, type_pos, 0);
}
@@ -4147,18 +4274,33 @@ load_message (DBusMessageLoader *loader,
/* 2. VALIDATE BODY */
if (mode != DBUS_VALIDATION_MODE_WE_TRUST_THIS_DATA_ABSOLUTELY)
{
- get_const_signature (&message->header, &type_str, &type_pos);
-
- /* Because the bytes_remaining arg is NULL, this validates that the
- * body is the right length
- */
- validity = _dbus_validate_body_with_reason (type_str,
- type_pos,
- byte_order,
- NULL,
- &loader->data,
- header_len,
- body_len);
+ if (_dbus_message_is_gvariant (message))
+ {
+ validity = _dbus_validate_gvariant_body_with_reason (type_str,
+ type_pos,
+ byte_order,
+ NULL,
+ &loader->data,
+ header_len,
+ body_len);
+ }
+ else
+ {
+
+ get_const_signature (message, &type_str, &type_pos);
+
+ /* Because the bytes_remaining arg is NULL, this validates that the
+ * body is the right length
+ */
+
+ validity = _dbus_validate_body_with_reason (type_str,
+ type_pos,
+ byte_order,
+ NULL,
+ &loader->data,
+ header_len,
+ body_len);
+ }
if (validity != DBUS_VALID)
{
_dbus_verbose ("Failed to validate message body code %d\n", validity);
@@ -4277,6 +4419,31 @@ load_message (DBusMessageLoader *loader,
return FALSE;
}
+static dbus_bool_t
+set_unique_sender (DBusMessage *message, uint64_t unique_sender_id)
+{
+ if (NULL == message->unique_sender)
+ {
+ message->unique_sender = dbus_new (DBusString, 1);
+ if (NULL == message->unique_sender)
+ return FALSE;
+
+ if (!_dbus_string_init (message->unique_sender))
+ return FALSE;
+ }
+
+ _dbus_string_set_length (message->unique_sender, 0);
+
+ if (!_dbus_string_append_printf (message->unique_sender, ":1.%llu", (unsigned long long)unique_sender_id))
+ {
+ _dbus_string_free (message->unique_sender);
+ message->unique_sender = NULL;
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
/**
* Converts buffered data into messages, if we have enough data. If
* we don't have enough data, does nothing.
@@ -4299,6 +4466,7 @@ _dbus_message_loader_queue_messages (DBusMessageLoader *loader)
{
DBusValidity validity;
int byte_order, fields_array_len, header_len, body_len;
+ dbus_bool_t is_gvariant;
if (_dbus_header_have_message_untrusted (loader->max_message_size,
&validity,
@@ -4307,13 +4475,14 @@ _dbus_message_loader_queue_messages (DBusMessageLoader *loader)
&header_len,
&body_len,
&loader->data, 0,
- _dbus_string_get_length (&loader->data)))
+ _dbus_string_get_length (&loader->data),
+ &is_gvariant))
{
DBusMessage *message;
_dbus_assert (validity == DBUS_VALID);
- message = dbus_message_new_empty_header ();
+ message = dbus_message_new_empty_header (is_gvariant);
if (message == NULL)
return FALSE;
@@ -4328,6 +4497,12 @@ _dbus_message_loader_queue_messages (DBusMessageLoader *loader)
return loader->corrupted;
}
+ if (_dbus_message_is_gvariant (message))
+ {
+ set_unique_sender (message, _dbus_message_loader_get_unique_sender_id (loader));
+ message->locked = TRUE;
+ }
+
_dbus_assert (loader->messages != NULL);
_dbus_assert (_dbus_list_find_last (&loader->messages, message) != NULL);
}
@@ -4498,6 +4673,17 @@ _dbus_message_loader_get_max_message_unix_fds (DBusMessageLoader *loader)
return loader->max_message_unix_fds;
}
+void _dbus_message_loader_set_unique_sender_id (DBusMessageLoader *loader,
+ uint64_t id)
+{
+ loader->unique_sender_id = id;
+}
+
+uint64_t _dbus_message_loader_get_unique_sender_id (DBusMessageLoader *loader)
+{
+ return loader->unique_sender_id;
+}
+
static DBusDataSlotAllocator slot_allocator =
_DBUS_DATA_SLOT_ALLOCATOR_INIT (_DBUS_LOCK_NAME (message_slots));
@@ -4804,6 +4990,7 @@ dbus_message_demarshal_bytes_needed(const char *buf,
int byte_order, fields_array_len, header_len, body_len;
DBusValidity validity = DBUS_VALID;
int have_message;
+ dbus_bool_t is_gvariant;
if (!buf || len < DBUS_MINIMUM_HEADER_SIZE)
return 0;
@@ -4820,7 +5007,8 @@ dbus_message_demarshal_bytes_needed(const char *buf,
&header_len,
&body_len,
&str, 0,
- len);
+ len,
+ &is_gvariant);
_dbus_string_free (&str);
if (validity == DBUS_VALID)
@@ -4835,6 +5023,224 @@ dbus_message_demarshal_bytes_needed(const char *buf,
}
}
+static dbus_bool_t
+_dbus_message_copy_recursive(DBusMessageIter *iter, DBusMessageIter *dest)
+{
+ dbus_bool_t res = TRUE;
+ int current_type;
+
+ while ((current_type = dbus_message_iter_get_arg_type (iter)) != DBUS_TYPE_INVALID) {
+ if (dbus_type_is_basic(current_type)) {
+ DBusBasicValue value;
+ dbus_message_iter_get_basic (iter, &value);
+ dbus_message_iter_append_basic (dest, current_type, &value);
+ }
+ else {
+ DBusMessageIter sub;
+ DBusMessageIter dest_sub;
+ char *sig = NULL;
+
+ dbus_message_iter_recurse (iter, &sub);
+ if (DBUS_TYPE_VARIANT == current_type)
+ sig = dbus_message_iter_get_signature (&sub);
+ else if (DBUS_TYPE_ARRAY == current_type)
+ sig = dbus_message_iter_get_signature (&sub);
+
+ res = res && dbus_message_iter_open_container (dest, current_type, sig, &dest_sub);
+ dbus_free(sig);
+ res = res && _dbus_message_copy_recursive (&sub, &dest_sub);
+ res = res && dbus_message_iter_close_container (dest, &dest_sub);
+
+ if (!res) {
+ return FALSE;
+ }
+ }
+
+ dbus_message_iter_next (iter);
+ }
+
+ return TRUE;
+}
+
+DBusMessage *
+_dbus_message_remarshal (DBusMessage *message, dbus_bool_t gvariant)
+{
+ DBusMessage *ret;
+ DBusMessageIter iter, ret_iter;
+ int i;
+ dbus_uint32_t serial;
+ const char *sender;
+
+ _dbus_assert (message->locked);
+
+ ret = _dbus_message_create_protocol_version (dbus_message_get_type(message),
+ dbus_message_get_destination(message),
+ dbus_message_get_path(message),
+ dbus_message_get_interface(message),
+ dbus_message_get_member(message),
+ dbus_message_get_error_name(message),
+ gvariant);
+
+ dbus_message_iter_init (message, &iter);
+ dbus_message_iter_init_append (ret, &ret_iter);
+ if (!_dbus_message_copy_recursive(&iter, &ret_iter))
+ return NULL;
+
+#ifdef HAVE_UNIX_FD_PASSING
+ ret->unix_fds = dbus_new(int, message->n_unix_fds);
+ if (ret->unix_fds == NULL && message->n_unix_fds > 0)
+ goto err;
+
+ ret->n_unix_fds_allocated = message->n_unix_fds;
+
+ for (i = 0; i < message->n_unix_fds; ++i) {
+ ret->unix_fds[i] = _dbus_dup(message->unix_fds[i], NULL);
+
+ if (ret->unix_fds[i] < 0)
+ goto err;
+ }
+
+ ret->n_unix_fds = message->n_unix_fds;
+#endif
+
+ /* Remarshal data in header:
+ byte order (already set)
+ type (already set)
+ flags - only those we understand
+ version (already set)
+ body length
+ serial
+ fields array (length)
+ fields:
+ path (already set)
+ interface (already set)
+ member (already set)
+ error name (already set)
+ reply serial
+ destination (already set)
+ sender
+ signature (set during copy, but an action needed for conversion to GVariant)
+ unix fds
+ */
+
+ /* FLAGS */
+ _dbus_header_toggle_flag (&ret->header, DBUS_HEADER_FLAG_NO_REPLY_EXPECTED,
+ _dbus_header_get_flag (&message->header, DBUS_HEADER_FLAG_NO_REPLY_EXPECTED));
+
+ _dbus_header_toggle_flag (&ret->header, DBUS_HEADER_FLAG_NO_AUTO_START,
+ _dbus_header_get_flag (&message->header, DBUS_HEADER_FLAG_NO_AUTO_START));
+
+ /* SERIAL / COOKIE */
+ serial = dbus_message_get_serial (message);
+
+ if (0 != serial)
+ dbus_message_set_serial (ret, serial);
+
+ /* Field: REPLY_SERIAL */
+ serial = dbus_message_get_reply_serial (message);
+
+ if (0 != serial && !dbus_message_set_reply_serial (ret, serial))
+ goto err;
+
+ /* Field: SENDER */
+ sender = dbus_message_get_sender (message);
+
+ if (NULL != sender && !dbus_message_set_sender (ret, sender))
+ goto err;
+
+ /* BODY LENGTH */
+ if (!gvariant)
+ _dbus_header_update_lengths (&ret->header,
+ _dbus_string_get_length (&ret->body));
+ /* For GVariant: */
+ /* Field: SIGNATURE to body; add body offset - this is done with dbus_message_lock() */
+
+ return ret;
+
+err:
+ _dbus_header_free (&ret->header);
+ _dbus_string_free (&ret->body);
+
+#ifdef HAVE_UNIX_FD_PASSING
+ close_unix_fds(ret->unix_fds, &ret->n_unix_fds);
+ dbus_free(ret->unix_fds);
+#endif
+
+ return NULL;
+}
+
+void
+dbus_set_protocol_version (unsigned char version)
+{
+ _dbus_default_protocol_version = version;
+}
+
+DBusMessage *
+_dbus_generate_local_error_message (dbus_uint32_t serial,
+ char *error_name,
+ char *error_msg)
+{
+ DBusMessage *message;
+ message = dbus_message_new (DBUS_MESSAGE_TYPE_ERROR);
+ if (!message)
+ goto out;
+
+ if (!dbus_message_set_error_name (message, error_name))
+ {
+ dbus_message_unref (message);
+ message = NULL;
+ goto out;
+ }
+
+ dbus_message_set_no_reply (message, TRUE);
+
+ if (!dbus_message_set_reply_serial (message,
+ serial))
+ {
+ dbus_message_unref (message);
+ message = NULL;
+ goto out;
+ }
+
+ if (error_msg != NULL)
+ {
+ DBusMessageIter iter;
+
+ dbus_message_iter_init_append (message, &iter);
+ if (!dbus_message_iter_append_basic (&iter,
+ DBUS_TYPE_STRING,
+ &error_msg))
+ {
+ dbus_message_unref (message);
+ message = NULL;
+ goto out;
+ }
+ }
+
+ out:
+ return message;
+}
+
+dbus_bool_t
+_dbus_message_assure_dbus1 (DBusMessage **message)
+{
+ if ((*message)->header.protocol_version != DBUS_MAJOR_PROTOCOL_VERSION)
+ {
+ *message = _dbus_message_remarshal (*message, FALSE);
+ }
+ return *message != NULL;
+}
+
+dbus_bool_t
+_dbus_message_assure_gvariant (DBusMessage **message)
+{
+ if ((*message)->header.protocol_version != DBUS_PROTOCOL_VERSION_GVARIANT)
+ {
+ *message = _dbus_message_remarshal (*message, TRUE);
+ }
+ return *message != NULL;
+}
+
/** @} */
/* tests in dbus-message-util.c */
diff --git a/dbus/dbus-message.h b/dbus/dbus-message.h
index 4fd44dab..007a2d8c 100644
--- a/dbus/dbus-message.h
+++ b/dbus/dbus-message.h
@@ -2,6 +2,7 @@
/* dbus-message.h DBusMessage object
*
* Copyright (C) 2002, 2003, 2005 Red Hat Inc.
+ * Copyright (C) 2015 Samsung Electronics
*
* Licensed under the Academic Free License version 2.1
*
@@ -49,21 +50,72 @@ typedef struct DBusMessageIter DBusMessageIter;
* DBusMessageIter struct; contains no public fields.
*/
struct DBusMessageIter
-{
- void *dummy1; /**< Don't use this */
- void *dummy2; /**< Don't use this */
- dbus_uint32_t dummy3; /**< Don't use this */
- int dummy4; /**< Don't use this */
- int dummy5; /**< Don't use this */
- int dummy6; /**< Don't use this */
- int dummy7; /**< Don't use this */
- int dummy8; /**< Don't use this */
- int dummy9; /**< Don't use this */
- int dummy10; /**< Don't use this */
- int dummy11; /**< Don't use this */
- int pad1; /**< Don't use this */
- int pad2; /**< Don't use this */
- void *pad3; /**< Don't use this */
+{ /* layout on a standard 64-bit system */
+ void *dummy1; /**< Don't use this */ /* message */
+ dbus_uint32_t dummy3a : 21; /**< Don't use this */
+ dbus_uint32_t dummy3b : 3; /**< Don't use this */
+ dbus_uint32_t dummy3c : 8; /**< Don't use this */
+
+ /* padding before union */
+ union {
+ struct
+ {
+ dbus_uint32_t dummy1a : 8; /**< Don't use this */
+ dbus_uint32_t dummy1b : 1; /**< Don't use this */
+ dbus_uint32_t dummy1c : 3; /**< Don't use this */
+ dbus_uint32_t dummy1d : 1; /**< Don't use this */
+ dbus_uint32_t dummy1e : 1; /**< Don't use this */
+ dbus_uint32_t dummy1f : 1; /**< Don't use this */
+ void *dummy2; /**< Don't use this */
+ int dummy3; /**< Don't use this */
+ void *dummy4; /**< Don't use this */
+ int dummy5; /**< Don't use this */
+ int dummy6; /**< Don't use this */
+ size_t dummy7; /**< Don't use this */
+ size_t dummy8; /**< Don't use this */
+ int dummy9; /**< Don't use this */
+
+ void *dummy10; /**< Don't use this */
+ union
+ {
+ struct {
+ int dummy11; /**< Don't use this */
+ };
+ } u;
+ } s1;
+
+ struct
+ {
+ dbus_uint32_t dummy1a : 8; /**< Don't use this */
+ dbus_uint32_t dummy1b : 8; /**< Don't use this */
+ dbus_uint32_t dummy1c : 1; /**< Don't use this */
+ dbus_uint32_t dummy1d : 1; /**< Don't use this */
+ dbus_uint32_t dummy1e : 1; /**< Don't use this */
+ dbus_uint32_t dummy1f : 1; /**< Don't use this */
+ dbus_uint32_t dummy1g : 1; /**< Don't use this */
+
+ void *dummy2; /**< Don't use this */
+ int dummy3; /**< Don't use this */
+ void *dummy4; /**< Don't use this */
+ int dummy5; /**< Don't use this */
+ size_t dummy6; /**< Don't use this */
+ void *dummy7; /**< Don't use this */
+ int dummy8; /**< Don't use this */
+ char dummy9; /**< Don't use this */
+
+ union
+ {
+ struct {
+ int dummy10; /**< Don't use this */
+ int dummy11; /**< Don't use this */
+ int dummy12; /**< Don't use this */
+ };
+ struct {
+ size_t dummy13; /**< Don't use this */
+ };
+ } u;
+ } s2;
+ } u;
};
DBUS_EXPORT
@@ -302,6 +354,9 @@ DBUS_EXPORT
int dbus_message_demarshal_bytes_needed (const char *str,
int len);
+DBUS_EXPORT
+void dbus_set_protocol_version (unsigned char version);
+
/** @} */
DBUS_END_DECLS
diff --git a/dbus/dbus-pending-call.c b/dbus/dbus-pending-call.c
index be534105..ed96242e 100644
--- a/dbus/dbus-pending-call.c
+++ b/dbus/dbus-pending-call.c
@@ -367,6 +367,9 @@ _dbus_pending_call_set_timeout_error_unlocked (DBusPendingCall *pending,
if (reply == NULL)
return FALSE;
+ /* FIXME - lock may now fail */
+ dbus_message_lock (reply);
+
reply_link = _dbus_list_alloc_link (reply);
if (reply_link == NULL)
{
diff --git a/dbus/dbus-protocol-gvariant.h b/dbus/dbus-protocol-gvariant.h
new file mode 100644
index 00000000..c01f9235
--- /dev/null
+++ b/dbus/dbus-protocol-gvariant.h
@@ -0,0 +1,56 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-protocol.h D-Bus protocol constants
+ *
+ * Copyright (C) 2015 Samsung Electronics
+ *
+ * Licensed under the Academic Free License version 2.1
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifndef DBUS_PROTOCOL_GVARIANT_H
+#define DBUS_PROTOCOL_GVARIANT_H
+
+#define DBUS_PROTOCOL_VERSION_GVARIANT 2
+
+/** Header format is defined as a signature:
+ * byte byte order
+ * byte message type ID
+ * byte flags
+ * byte protocol version
+ * uint64 cookie
+ * array of dict entries (uint64,variant) (field name, value)
+ *
+ * The length of the header can be computed as the
+ * fixed size of the initial data, plus the length of
+ * the array at the end, plus padding to an 8-boundary.
+ */
+#define DBUS_HEADER_GVARIANT_SIGNATURE \
+ DBUS_TYPE_BYTE_AS_STRING \
+ DBUS_TYPE_BYTE_AS_STRING \
+ DBUS_TYPE_BYTE_AS_STRING \
+ DBUS_TYPE_BYTE_AS_STRING \
+ DBUS_TYPE_UINT64_AS_STRING \
+ DBUS_TYPE_ARRAY_AS_STRING \
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING \
+ DBUS_TYPE_BYTE_AS_STRING \
+ DBUS_TYPE_VARIANT_AS_STRING \
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING \
+ DBUS_TYPE_VARIANT_AS_STRING
+
+#define FIRST_GVARIANT_FIELD_OFFSET 16 /* yyyyut is before a{tv}*/
+
+#endif /* DBUS_PROTOCOL_GVARIANT_H */
diff --git a/dbus/dbus-shared.h b/dbus/dbus-shared.h
index 5371d886..b16d35e7 100644
--- a/dbus/dbus-shared.h
+++ b/dbus/dbus-shared.h
@@ -120,6 +120,8 @@ typedef enum
#define DBUS_START_REPLY_SUCCESS 1 /**< Service was auto started */
#define DBUS_START_REPLY_ALREADY_RUNNING 2 /**< Service was already running */
+#define DBUS_ADDRESS_KDBUS "kernel:"
+
/** @} */
#ifdef __cplusplus
diff --git a/dbus/dbus-signals.c b/dbus/dbus-signals.c
new file mode 100644
index 00000000..e0496b17
--- /dev/null
+++ b/dbus/dbus-signals.c
@@ -0,0 +1,1633 @@
+/* signals.c Bus signal connection implementation
+ *
+ * Copyright (C) 2003, 2005 Red Hat, Inc.
+ * Copyright 2014 Samsung Electronics
+ *
+ * Licensed under the Academic Free License version 2.1
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include "../config.h"
+#include "dbus-signals.h"
+#include <dbus/dbus-marshal-validate.h>
+#include "dbus-internals.h"
+#include "dbus-hash.h"
+#include "dbus-list.h"
+#include "kdbus-common.h"
+#include <stdlib.h>
+#include <limits.h>
+
+#define SET_OOM(error) dbus_set_error_const ((error), DBUS_ERROR_NO_MEMORY, "Memory allocation failure in transport, regarding match rules")
+
+struct MatchRule
+{
+ int refcount; /**< reference count */
+
+ DBusConnection *matches_go_to; /**< Owner of the rule */
+
+ unsigned int flags; /**< MatchFlags */
+
+ int message_type;
+ char *interface;
+ char *member;
+ char *sender;
+ char *destination;
+ char *path;
+
+ unsigned int *arg_lens;
+ char **args;
+ int args_len;
+
+ __u64 kdbus_cookie;
+};
+
+#define MATCH_ARG_FLAGS (MATCH_ARG_NAMESPACE |MATCH_ARG_IS_PATH)
+
+static MatchRule*
+bus_match_rule_new (DBusConnection *matches_go_to)
+{
+ MatchRule *rule;
+
+ rule = dbus_new0 (MatchRule, 1);
+ if (rule == NULL)
+ return NULL;
+
+ rule->refcount = 1;
+ rule->matches_go_to = matches_go_to;
+ rule->kdbus_cookie = 0;
+
+#ifndef DBUS_ENABLE_EMBEDDED_TESTS
+ _dbus_assert (rule->matches_go_to != NULL);
+#endif
+
+ return rule;
+}
+
+static MatchRule *
+bus_match_rule_ref (MatchRule *rule)
+{
+ _dbus_assert (rule->refcount > 0);
+
+ rule->refcount += 1;
+
+ return rule;
+}
+
+void
+match_rule_unref (MatchRule *rule)
+{
+ _dbus_assert (rule->refcount > 0);
+
+ rule->refcount -= 1;
+ if (rule->refcount == 0)
+ {
+ dbus_free (rule->interface);
+ dbus_free (rule->member);
+ dbus_free (rule->sender);
+ dbus_free (rule->destination);
+ dbus_free (rule->path);
+ dbus_free (rule->arg_lens);
+
+ /* can't use dbus_free_string_array() since there
+ * are embedded NULL
+ */
+ if (rule->args)
+ {
+ int i;
+
+ i = 0;
+ while (i < rule->args_len)
+ {
+ if (rule->args[i])
+ dbus_free (rule->args[i]);
+ ++i;
+ }
+
+ dbus_free (rule->args);
+ }
+
+ dbus_free (rule);
+ }
+}
+
+#ifdef DBUS_ENABLE_VERBOSE_MODE
+/* Note this function does not do escaping, so it's only
+ * good for debug spew at the moment
+ */
+char*
+match_rule_to_string (MatchRule *rule)
+{
+ DBusString str;
+ char *ret;
+
+ if (!_dbus_string_init (&str))
+ {
+ char *s;
+ while ((s = _dbus_strdup ("nomem")) == NULL)
+ ; /* only OK for debug spew... */
+ return s;
+ }
+
+ if (rule->flags & MATCH_MESSAGE_TYPE)
+ {
+ if (!_dbus_string_append_printf (&str, "type='%s'",
+ dbus_message_type_to_string (rule->message_type)))
+ goto nomem;
+ }
+
+ if (rule->flags & MATCH_INTERFACE)
+ {
+ if (_dbus_string_get_length (&str) > 0)
+ {
+ if (!_dbus_string_append (&str, ","))
+ goto nomem;
+ }
+
+ if (!_dbus_string_append_printf (&str, "interface='%s'", rule->interface))
+ goto nomem;
+ }
+
+ if (rule->flags & MATCH_MEMBER)
+ {
+ if (_dbus_string_get_length (&str) > 0)
+ {
+ if (!_dbus_string_append (&str, ","))
+ goto nomem;
+ }
+
+ if (!_dbus_string_append_printf (&str, "member='%s'", rule->member))
+ goto nomem;
+ }
+
+ if (rule->flags & MATCH_PATH)
+ {
+ if (_dbus_string_get_length (&str) > 0)
+ {
+ if (!_dbus_string_append (&str, ","))
+ goto nomem;
+ }
+
+ if (!_dbus_string_append_printf (&str, "path='%s'", rule->path))
+ goto nomem;
+ }
+
+ if (rule->flags & MATCH_PATH_NAMESPACE)
+ {
+ if (_dbus_string_get_length (&str) > 0)
+ {
+ if (!_dbus_string_append (&str, ","))
+ goto nomem;
+ }
+
+ if (!_dbus_string_append_printf (&str, "path_namespace='%s'", rule->path))
+ goto nomem;
+ }
+
+ if (rule->flags & MATCH_SENDER)
+ {
+ if (_dbus_string_get_length (&str) > 0)
+ {
+ if (!_dbus_string_append (&str, ","))
+ goto nomem;
+ }
+
+ if (!_dbus_string_append_printf (&str, "sender='%s'", rule->sender))
+ goto nomem;
+ }
+
+ if (rule->flags & MATCH_DESTINATION)
+ {
+ if (_dbus_string_get_length (&str) > 0)
+ {
+ if (!_dbus_string_append (&str, ","))
+ goto nomem;
+ }
+
+ if (!_dbus_string_append_printf (&str, "destination='%s'", rule->destination))
+ goto nomem;
+ }
+
+ if (rule->flags & MATCH_CLIENT_IS_EAVESDROPPING)
+ {
+ if (_dbus_string_get_length (&str) > 0)
+ {
+ if (!_dbus_string_append (&str, ","))
+ goto nomem;
+ }
+
+ if (!_dbus_string_append_printf (&str, "eavesdrop='%s'",
+ (rule->flags & MATCH_CLIENT_IS_EAVESDROPPING) ?
+ "true" : "false"))
+ goto nomem;
+ }
+
+ if (rule->flags &MATCH_ARGS)
+ {
+ int i;
+
+ _dbus_assert (rule->args != NULL);
+
+ i = 0;
+ while (i < rule->args_len)
+ {
+ if (rule->args[i] != NULL)
+ {
+ dbus_bool_t is_path, is_namespace;
+
+ if (_dbus_string_get_length (&str) > 0)
+ {
+ if (!_dbus_string_append (&str, ","))
+ goto nomem;
+ }
+
+ is_path = (rule->arg_lens[i] & MATCH_ARG_IS_PATH) != 0;
+ is_namespace = (rule->arg_lens[i] & MATCH_ARG_NAMESPACE) != 0;
+
+ if (!_dbus_string_append_printf (&str,
+ "arg%d%s='%s'",
+ i,
+ is_path ? "path" :
+ is_namespace ? "namespace" : "",
+ rule->args[i]))
+ goto nomem;
+ }
+
+ ++i;
+ }
+ }
+
+ if (!_dbus_string_steal_data (&str, &ret))
+ goto nomem;
+
+ _dbus_string_free (&str);
+ return ret;
+
+ nomem:
+ _dbus_string_free (&str);
+ {
+ char *s;
+ while ((s = _dbus_strdup ("nomem")) == NULL)
+ ; /* only OK for debug spew... */
+ return s;
+ }
+}
+#endif /* DBUS_ENABLE_VERBOSE_MODE */
+
+static dbus_bool_t
+bus_match_rule_set_message_type (MatchRule *rule,
+ int type)
+{
+ rule->flags |=MATCH_MESSAGE_TYPE;
+
+ rule->message_type = type;
+
+ return TRUE;
+}
+
+static dbus_bool_t
+bus_match_rule_set_interface (MatchRule *rule,
+ const char *interface)
+{
+ char *new;
+
+ _dbus_assert (interface != NULL);
+
+ new = _dbus_strdup (interface);
+ if (new == NULL)
+ return FALSE;
+
+ rule->flags |=MATCH_INTERFACE;
+ dbus_free (rule->interface);
+ rule->interface = new;
+
+ return TRUE;
+}
+
+static dbus_bool_t
+bus_match_rule_set_member (MatchRule *rule,
+ const char *member)
+{
+ char *new;
+
+ _dbus_assert (member != NULL);
+
+ new = _dbus_strdup (member);
+ if (new == NULL)
+ return FALSE;
+
+ rule->flags |=MATCH_MEMBER;
+ dbus_free (rule->member);
+ rule->member = new;
+
+ return TRUE;
+}
+
+static dbus_bool_t
+bus_match_rule_set_sender (MatchRule *rule,
+ const char *sender)
+{
+ char *new;
+
+ _dbus_assert (sender != NULL);
+
+ new = _dbus_strdup (sender);
+ if (new == NULL)
+ return FALSE;
+
+ rule->flags |=MATCH_SENDER;
+ dbus_free (rule->sender);
+ rule->sender = new;
+
+ return TRUE;
+}
+
+static dbus_bool_t
+bus_match_rule_set_destination (MatchRule *rule,
+ const char *destination)
+{
+ char *new;
+
+ _dbus_assert (destination != NULL);
+
+ new = _dbus_strdup (destination);
+ if (new == NULL)
+ return FALSE;
+
+ rule->flags |=MATCH_DESTINATION;
+ dbus_free (rule->destination);
+ rule->destination = new;
+
+ return TRUE;
+}
+
+static void
+bus_match_rule_set_client_is_eavesdropping (MatchRule *rule,
+ dbus_bool_t is_eavesdropping)
+{
+ if (is_eavesdropping)
+ rule->flags |= MATCH_CLIENT_IS_EAVESDROPPING;
+ else
+ rule->flags &= ~(MATCH_CLIENT_IS_EAVESDROPPING);
+}
+
+static dbus_bool_t
+bus_match_rule_set_path (MatchRule *rule,
+ const char *path,
+ dbus_bool_t is_namespace)
+{
+ char *new;
+
+ _dbus_assert (path != NULL);
+
+ new = _dbus_strdup (path);
+ if (new == NULL)
+ return FALSE;
+
+ rule->flags &= ~(MATCH_PATH | MATCH_PATH_NAMESPACE);
+
+ if (is_namespace)
+ rule->flags |= MATCH_PATH_NAMESPACE;
+ else
+ rule->flags |= MATCH_PATH;
+
+ dbus_free (rule->path);
+ rule->path = new;
+
+ return TRUE;
+}
+
+static dbus_bool_t
+bus_match_rule_set_arg (MatchRule *rule,
+ int arg,
+ const DBusString *value,
+ dbus_bool_t is_path,
+ dbus_bool_t is_namespace)
+{
+ int length;
+ char *new;
+
+ _dbus_assert (value != NULL);
+
+ /* args_len is the number of args not including null termination
+ * in the char**
+ */
+ if (arg >= rule->args_len)
+ {
+ unsigned int *new_arg_lens;
+ char **new_args;
+ int new_args_len;
+ int i;
+
+ new_args_len = arg + 1;
+
+ /* add another + 1 here for null termination */
+ new_args = dbus_realloc (rule->args,
+ sizeof (char *) * (new_args_len + 1));
+ if (new_args == NULL)
+ return FALSE;
+
+ /* NULL the new slots */
+ i = rule->args_len;
+ while (i <= new_args_len) /* <= for null termination */
+ {
+ new_args[i] = NULL;
+ ++i;
+ }
+
+ rule->args = new_args;
+
+ /* and now add to the lengths */
+ new_arg_lens = dbus_realloc (rule->arg_lens,
+ sizeof (int) * (new_args_len + 1));
+
+ if (new_arg_lens == NULL)
+ return FALSE;
+
+ /* zero the new slots */
+ i = rule->args_len;
+ while (i <= new_args_len) /* <= for null termination */
+ {
+ new_arg_lens[i] = 0;
+ ++i;
+ }
+
+ rule->arg_lens = new_arg_lens;
+ rule->args_len = new_args_len;
+ }
+
+ length = _dbus_string_get_length (value);
+ if (!_dbus_string_copy_data (value, &new))
+ return FALSE;
+
+ rule->flags |=MATCH_ARGS;
+
+ dbus_free (rule->args[arg]);
+ rule->arg_lens[arg] = length;
+ rule->args[arg] = new;
+
+ if (is_path)
+ rule->arg_lens[arg] |=MATCH_ARG_IS_PATH;
+
+ if (is_namespace)
+ rule->arg_lens[arg] |=MATCH_ARG_NAMESPACE;
+
+ /* NULL termination didn't get busted */
+ _dbus_assert (rule->args[rule->args_len] == NULL);
+ _dbus_assert (rule->arg_lens[rule->args_len] == 0);
+
+ return TRUE;
+}
+
+void
+match_rule_set_cookie (MatchRule *rule, dbus_uint64_t cookie)
+{
+ rule->kdbus_cookie = cookie;
+}
+
+dbus_uint64_t
+match_rule_get_cookie (MatchRule *rule)
+{
+ return rule->kdbus_cookie;
+}
+
+#define ISWHITE(c) (((c) == ' ') || ((c) == '\t') || ((c) == '\n') || ((c) == '\r'))
+
+static dbus_bool_t
+find_key (const DBusString *str,
+ int start,
+ DBusString *key,
+ int *value_pos,
+ DBusError *error)
+{
+ const char *p;
+ const char *s;
+ const char *key_start;
+ const char *key_end;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ s = _dbus_string_get_const_data (str);
+
+ p = s + start;
+
+ while (*p && ISWHITE (*p))
+ ++p;
+
+ key_start = p;
+
+ while (*p && *p != '=' && !ISWHITE (*p))
+ ++p;
+
+ key_end = p;
+
+ while (*p && ISWHITE (*p))
+ ++p;
+
+ if (key_start == key_end)
+ {
+ /* Empty match rules or trailing whitespace are OK */
+ *value_pos = p - s;
+ return TRUE;
+ }
+
+ if (*p != '=')
+ {
+ dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID,
+ "Match rule has a key with no subsequent '=' character");
+ return FALSE;
+ }
+ ++p;
+
+ if (!_dbus_string_append_len (key, key_start, key_end - key_start))
+ {
+ SET_OOM (error);
+ return FALSE;
+ }
+
+ *value_pos = p - s;
+
+ return TRUE;
+}
+
+static dbus_bool_t
+find_value (const DBusString *str,
+ int start,
+ const char *key,
+ DBusString *value,
+ int *value_end,
+ DBusError *error)
+{
+ const char *p;
+ const char *s;
+ char quote_char;
+ int orig_len;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ orig_len = _dbus_string_get_length (value);
+
+ s = _dbus_string_get_const_data (str);
+
+ p = s + start;
+
+ quote_char = '\0';
+
+ while (*p)
+ {
+ if (quote_char == '\0')
+ {
+ switch (*p)
+ {
+ case '\0':
+ goto done;
+
+ case '\'':
+ quote_char = '\'';
+ goto next;
+
+ case ',':
+ ++p;
+ goto done;
+
+ case '\\':
+ quote_char = '\\';
+ goto next;
+
+ default:
+ if (!_dbus_string_append_byte (value, *p))
+ {
+ SET_OOM (error);
+ goto failed;
+ }
+ }
+ }
+ else if (quote_char == '\\')
+ {
+ /* \ only counts as an escape if escaping a quote mark */
+ if (*p != '\'')
+ {
+ if (!_dbus_string_append_byte (value, '\\'))
+ {
+ SET_OOM (error);
+ goto failed;
+ }
+ }
+
+ if (!_dbus_string_append_byte (value, *p))
+ {
+ SET_OOM (error);
+ goto failed;
+ }
+
+ quote_char = '\0';
+ }
+ else
+ {
+ _dbus_assert (quote_char == '\'');
+
+ if (*p == '\'')
+ {
+ quote_char = '\0';
+ }
+ else
+ {
+ if (!_dbus_string_append_byte (value, *p))
+ {
+ SET_OOM (error);
+ goto failed;
+ }
+ }
+ }
+
+ next:
+ ++p;
+ }
+
+ done:
+
+ if (quote_char == '\\')
+ {
+ if (!_dbus_string_append_byte (value, '\\'))
+ {
+ SET_OOM (error);
+ goto failed;
+ }
+ }
+ else if (quote_char == '\'')
+ {
+ dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID,
+ "Unbalanced quotation marks in match rule");
+ goto failed;
+ }
+ else
+ _dbus_assert (quote_char == '\0');
+
+ /* Zero-length values are allowed */
+
+ *value_end = p - s;
+
+ return TRUE;
+
+ failed:
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+ _dbus_string_set_length (value, orig_len);
+ return FALSE;
+}
+
+/* duplicates aren't allowed so the real legitimate max is only 6 or
+ * so. Leaving extra so we don't have to bother to update it.
+ * FIXME this is sort of busted now with arg matching, but we let
+ * you match on up to 10 args for now
+ */
+#define MAX_RULE_TOKENS 16
+
+/* this is slightly too high level to be termed a "token"
+ * but let's not be pedantic.
+ */
+typedef struct
+{
+ char *key;
+ char *value;
+} RuleToken;
+
+static dbus_bool_t
+tokenize_rule (const DBusString *rule_text,
+ RuleToken tokens[MAX_RULE_TOKENS],
+ DBusError *error)
+{
+ int i;
+ int pos;
+ DBusString key;
+ DBusString value;
+ dbus_bool_t retval;
+
+ retval = FALSE;
+
+ if (!_dbus_string_init (&key))
+ {
+ SET_OOM (error);
+ return FALSE;
+ }
+
+ if (!_dbus_string_init (&value))
+ {
+ _dbus_string_free (&key);
+ SET_OOM (error);
+ return FALSE;
+ }
+
+ i = 0;
+ pos = 0;
+ while (i < MAX_RULE_TOKENS &&
+ pos < _dbus_string_get_length (rule_text))
+ {
+ _dbus_assert (tokens[i].key == NULL);
+ _dbus_assert (tokens[i].value == NULL);
+
+ if (!find_key (rule_text, pos, &key, &pos, error))
+ goto out;
+
+ if (_dbus_string_get_length (&key) == 0)
+ goto next;
+
+ if (!_dbus_string_steal_data (&key, &tokens[i].key))
+ {
+ SET_OOM (error);
+ goto out;
+ }
+
+ if (!find_value (rule_text, pos, tokens[i].key, &value, &pos, error))
+ goto out;
+
+ if (!_dbus_string_steal_data (&value, &tokens[i].value))
+ {
+ SET_OOM (error);
+ goto out;
+ }
+
+ next:
+ ++i;
+ }
+
+ retval = TRUE;
+
+ out:
+ if (!retval)
+ {
+ i = 0;
+ while (tokens[i].key || tokens[i].value)
+ {
+ dbus_free (tokens[i].key);
+ dbus_free (tokens[i].value);
+ tokens[i].key = NULL;
+ tokens[i].value = NULL;
+ ++i;
+ }
+ }
+
+ _dbus_string_free (&key);
+ _dbus_string_free (&value);
+
+ return retval;
+}
+
+static dbus_bool_t
+bus_match_rule_parse_arg_match (MatchRule *rule,
+ const char *key,
+ const DBusString *value,
+ DBusError *error)
+{
+ dbus_bool_t is_path = FALSE;
+ dbus_bool_t is_namespace = FALSE;
+ DBusString key_str;
+ unsigned long arg;
+ int length;
+ int end;
+
+ /* For now, arg0='foo' always implies that 'foo' is a
+ * DBUS_TYPE_STRING. Someday we could add an arg0type='int32' thing
+ * if we wanted, which would specify another type, in which case
+ * arg0='5' would have the 5 parsed as an int rather than string.
+ */
+
+ /* First we need to parse arg0 = 0, arg27 = 27 */
+
+ _dbus_string_init_const (&key_str, key);
+ length = _dbus_string_get_length (&key_str);
+
+ if (_dbus_string_get_length (&key_str) < 4)
+ {
+ dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID,
+ "Key '%s' in match rule starts with 'arg' but lacks an arg number. Should be 'arg0' or 'arg7' for example.\n", key);
+ goto failed;
+ }
+
+ if (!_dbus_string_parse_uint (&key_str, 3, &arg, &end))
+ {
+ dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID,
+ "Key '%s' in match rule starts with 'arg' but could not parse arg number. Should be 'arg0' or 'arg7' for example.\n", key);
+ goto failed;
+ }
+
+ if (end != length)
+ {
+ if ((end + strlen ("path")) == length &&
+ _dbus_string_ends_with_c_str (&key_str, "path"))
+ {
+ is_path = TRUE;
+ }
+ else if (_dbus_string_equal_c_str (&key_str, "arg0namespace"))
+ {
+ int value_len = _dbus_string_get_length (value);
+
+ is_namespace = TRUE;
+
+ if (!_dbus_validate_bus_namespace (value, 0, value_len))
+ {
+ dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID,
+ "arg0namespace='%s' is not a valid prefix of a bus name",
+ _dbus_string_get_const_data (value));
+ goto failed;
+ }
+ }
+ else
+ {
+ dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID,
+ "Key '%s' in match rule contains junk after argument number (%u). Only 'arg%upath' (for example) or 'arg0namespace' are valid", key, arg, arg);
+ goto failed;
+ }
+ }
+
+ /* If we didn't check this we could allocate a huge amount of RAM */
+ if (arg > DBUS_MAXIMUM_MATCH_RULE_ARG_NUMBER)
+ {
+ dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID,
+ "Key '%s' in match rule has arg number %lu but the maximum is %d.\n", key, (unsigned long) arg, DBUS_MAXIMUM_MATCH_RULE_ARG_NUMBER);
+ goto failed;
+ }
+
+ if ((rule->flags &MATCH_ARGS) &&
+ rule->args_len > (int) arg &&
+ rule->args[arg] != NULL)
+ {
+ dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID,
+ "Argument %d matched more than once in match rule\n", key);
+ goto failed;
+ }
+
+ if (!bus_match_rule_set_arg (rule, arg, value, is_path, is_namespace))
+ {
+ SET_OOM (error);
+ goto failed;
+ }
+
+ return TRUE;
+
+ failed:
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+ return FALSE;
+}
+
+/*
+ * The format is comma-separated with strings quoted with single quotes
+ * as for the shell (to escape a literal single quote, use '\'').
+ *
+ * type='signal',sender='org.freedesktop.DBus',interface='org.freedesktop.DBus',member='Foo',
+ * path='/bar/foo',destination=':452345.34'
+ *
+ */
+MatchRule*
+match_rule_parse (DBusConnection *matches_go_to,
+ const DBusString *rule_text,
+ DBusError *error)
+{
+ MatchRule *rule;
+ RuleToken tokens[MAX_RULE_TOKENS+1]; /* NULL termination + 1 */
+ int i;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ if (_dbus_string_get_length (rule_text) > DBUS_MAXIMUM_MATCH_RULE_LENGTH)
+ {
+ dbus_set_error (error, DBUS_ERROR_LIMITS_EXCEEDED,
+ "Match rule text is %d bytes, maximum is %d",
+ _dbus_string_get_length (rule_text),
+ DBUS_MAXIMUM_MATCH_RULE_LENGTH);
+ return NULL;
+ }
+
+ memset (tokens, '\0', sizeof (tokens));
+
+ rule = bus_match_rule_new (matches_go_to);
+ if (rule == NULL)
+ {
+ SET_OOM (error);
+ goto failed;
+ }
+
+ if (!tokenize_rule (rule_text, tokens, error))
+ goto failed;
+
+ i = 0;
+ while (tokens[i].key != NULL)
+ {
+ DBusString tmp_str;
+ int len;
+ const char *key = tokens[i].key;
+ const char *value = tokens[i].value;
+
+ _dbus_string_init_const (&tmp_str, value);
+ len = _dbus_string_get_length (&tmp_str);
+
+ if (strcmp (key, "type") == 0)
+ {
+ int t;
+
+ if (rule->flags & MATCH_MESSAGE_TYPE)
+ {
+ dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID,
+ "Key %s specified twice in match rule\n", key);
+ goto failed;
+ }
+
+ t = dbus_message_type_from_string (value);
+
+ if (t == DBUS_MESSAGE_TYPE_INVALID)
+ {
+ dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID,
+ "Invalid message type (%s) in match rule\n", value);
+ goto failed;
+ }
+
+ if (!bus_match_rule_set_message_type (rule, t))
+ {
+ SET_OOM (error);
+ goto failed;
+ }
+ }
+ else if (strcmp (key, "sender") == 0)
+ {
+ if (rule->flags & MATCH_SENDER)
+ {
+ dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID,
+ "Key %s specified twice in match rule\n", key);
+ goto failed;
+ }
+
+ if (!_dbus_validate_bus_name (&tmp_str, 0, len))
+ {
+ dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID,
+ "Sender name '%s' is invalid\n", value);
+ goto failed;
+ }
+
+ if (!bus_match_rule_set_sender (rule, value))
+ {
+ SET_OOM (error);
+ goto failed;
+ }
+ }
+ else if (strcmp (key, "interface") == 0)
+ {
+ if (rule->flags & MATCH_INTERFACE)
+ {
+ dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID,
+ "Key %s specified twice in match rule\n", key);
+ goto failed;
+ }
+
+ if (!_dbus_validate_interface (&tmp_str, 0, len))
+ {
+ dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID,
+ "Interface name '%s' is invalid\n", value);
+ goto failed;
+ }
+
+ if (!bus_match_rule_set_interface (rule, value))
+ {
+ SET_OOM (error);
+ goto failed;
+ }
+ }
+ else if (strcmp (key, "member") == 0)
+ {
+ if (rule->flags & MATCH_MEMBER)
+ {
+ dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID,
+ "Key %s specified twice in match rule\n", key);
+ goto failed;
+ }
+
+ if (!_dbus_validate_member (&tmp_str, 0, len))
+ {
+ dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID,
+ "Member name '%s' is invalid\n", value);
+ goto failed;
+ }
+
+ if (!bus_match_rule_set_member (rule, value))
+ {
+ SET_OOM (error);
+ goto failed;
+ }
+ }
+ else if (strcmp (key, "path") == 0 ||
+ strcmp (key, "path_namespace") == 0)
+ {
+ dbus_bool_t is_namespace = (strcmp (key, "path_namespace") == 0);
+
+ if (rule->flags & (MATCH_PATH | MATCH_PATH_NAMESPACE))
+ {
+ dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID,
+ "path or path_namespace specified twice in match rule\n");
+ goto failed;
+ }
+
+ if (!_dbus_validate_path (&tmp_str, 0, len))
+ {
+ dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID,
+ "Path '%s' is invalid\n", value);
+ goto failed;
+ }
+
+ if (!bus_match_rule_set_path (rule, value, is_namespace))
+ {
+ SET_OOM (error);
+ goto failed;
+ }
+ }
+ else if (strcmp (key, "destination") == 0)
+ {
+ if (rule->flags & MATCH_DESTINATION)
+ {
+ dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID,
+ "Key %s specified twice in match rule\n", key);
+ goto failed;
+ }
+
+ if (!_dbus_validate_bus_name (&tmp_str, 0, len))
+ {
+ dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID,
+ "Destination name '%s' is invalid\n", value);
+ goto failed;
+ }
+
+ if (!bus_match_rule_set_destination (rule, value))
+ {
+ SET_OOM (error);
+ goto failed;
+ }
+ }
+ else if (strcmp (key, "eavesdrop") == 0)
+ {
+ /* do not detect "eavesdrop" being used more than once in rule:
+ * 1) it's not possible, it's only in the flags
+ * 2) it might be used twice to disable eavesdropping when it's
+ * automatically added (eg dbus-monitor/bustle) */
+
+ /* we accept only "true|false" as possible values */
+ if ((strcmp (value, "true") == 0))
+ {
+ bus_match_rule_set_client_is_eavesdropping (rule, TRUE);
+ }
+ else if (strcmp (value, "false") == 0)
+ {
+ bus_match_rule_set_client_is_eavesdropping (rule, FALSE);
+ }
+ else
+ {
+ dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID,
+ "eavesdrop='%s' is invalid, "
+ "it should be 'true' or 'false'\n",
+ value);
+ goto failed;
+ }
+ }
+ else if (strncmp (key, "arg", 3) == 0)
+ {
+ if (!bus_match_rule_parse_arg_match (rule, key, &tmp_str, error))
+ goto failed;
+ }
+ else
+ {
+ dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID,
+ "Unknown key \"%s\" in match rule",
+ key);
+ goto failed;
+ }
+
+ ++i;
+ }
+
+
+ goto out;
+
+ failed:
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+ if (rule)
+ {
+ match_rule_unref (rule);
+ rule = NULL;
+ }
+
+ out:
+
+ i = 0;
+ while (tokens[i].key || tokens[i].value)
+ {
+ _dbus_assert (i < MAX_RULE_TOKENS);
+ dbus_free (tokens[i].key);
+ dbus_free (tokens[i].value);
+ ++i;
+ }
+
+ return rule;
+}
+
+typedef struct RulePool RulePool;
+struct RulePool
+{
+ /* Maps non-NULL interface names to non-NULL (DBusList **)s */
+ DBusHashTable *rules_by_iface;
+
+ /* List of MatchRules which don't specify an interface */
+ DBusList *rules_without_iface;
+};
+
+struct Matchmaker
+{
+ int refcount;
+
+ /* Pools of rules, grouped by the type of message they match. 0
+ * (DBUS_MESSAGE_TYPE_INVALID) represents rules that do not specify a message
+ * type.
+ */
+ RulePool rules_by_type[DBUS_NUM_MESSAGE_TYPES];
+
+ int last_cookie;
+};
+
+static void
+rule_list_free (DBusList **rules)
+{
+ while (*rules != NULL)
+ {
+ MatchRule *rule;
+
+ rule = (*rules)->data;
+ match_rule_unref (rule);
+ _dbus_list_remove_link (rules, *rules);
+ }
+}
+
+static void
+rule_list_ptr_free (DBusList **list)
+{
+ /* We have to cope with NULL because the hash table frees the "existing"
+ * value (which is NULL) when creating a new table entry...
+ */
+ if (list != NULL)
+ {
+ rule_list_free (list);
+ dbus_free (list);
+ }
+}
+
+Matchmaker*
+matchmaker_new (void)
+{
+ Matchmaker *matchmaker;
+ int i;
+
+ matchmaker = dbus_new0 (Matchmaker, 1);
+ if (matchmaker == NULL)
+ return NULL;
+
+ matchmaker->refcount = 1;
+ matchmaker->last_cookie = 0;
+
+ for (i = DBUS_MESSAGE_TYPE_INVALID; i < DBUS_NUM_MESSAGE_TYPES; i++)
+ {
+ RulePool *p = matchmaker->rules_by_type + i;
+
+ p->rules_by_iface = _dbus_hash_table_new (DBUS_HASH_STRING,
+ dbus_free, (DBusFreeFunction) rule_list_ptr_free);
+
+ if (p->rules_by_iface == NULL)
+ goto nomem;
+ }
+
+ return matchmaker;
+
+ nomem:
+ for (i = DBUS_MESSAGE_TYPE_INVALID; i < DBUS_NUM_MESSAGE_TYPES; i++)
+ {
+ RulePool *p = matchmaker->rules_by_type + i;
+
+ if (p->rules_by_iface == NULL)
+ break;
+ else
+ _dbus_hash_table_unref (p->rules_by_iface);
+ }
+ dbus_free (matchmaker);
+
+ return NULL;
+}
+
+DBusList **
+matchmaker_get_rules (Matchmaker *matchmaker,
+ int message_type,
+ const char *interface,
+ dbus_bool_t create)
+{
+ RulePool *p;
+
+ _dbus_assert (message_type >= 0);
+ _dbus_assert (message_type < DBUS_NUM_MESSAGE_TYPES);
+
+ _dbus_verbose ("Looking up rules for message_type %d, interface %s\n",
+ message_type,
+ interface != NULL ? interface : "<null>");
+
+ p = matchmaker->rules_by_type + message_type;
+
+ if (interface == NULL)
+ {
+ return &p->rules_without_iface;
+ }
+ else
+ {
+ DBusList **list;
+
+ list = _dbus_hash_table_lookup_string (p->rules_by_iface, interface);
+
+ if (list == NULL && create)
+ {
+ char *dupped_interface;
+
+ list = dbus_new0 (DBusList *, 1);
+ if (list == NULL)
+ return NULL;
+
+ dupped_interface = _dbus_strdup (interface);
+ if (dupped_interface == NULL)
+ {
+ dbus_free (list);
+ return NULL;
+ }
+
+ _dbus_verbose ("Adding list for type %d, iface %s\n", message_type,
+ interface);
+
+ if (!_dbus_hash_table_insert_string (p->rules_by_iface,
+ dupped_interface, list))
+ {
+ dbus_free (list);
+ dbus_free (dupped_interface);
+ return NULL;
+ }
+ }
+
+ return list;
+ }
+}
+
+static void
+bus_matchmaker_gc_rules (Matchmaker *matchmaker,
+ int message_type,
+ const char *interface,
+ DBusList **rules)
+{
+ RulePool *p;
+
+ if (interface == NULL)
+ return;
+
+ if (*rules != NULL)
+ return;
+
+ _dbus_verbose ("GCing HT entry for message_type %u, interface %s\n",
+ message_type, interface);
+
+ p = matchmaker->rules_by_type + message_type;
+
+ _dbus_assert (_dbus_hash_table_lookup_string (p->rules_by_iface, interface)
+ == rules);
+
+ _dbus_hash_table_remove_string (p->rules_by_iface, interface);
+}
+
+/* The rule can't be modified after it's added. */
+dbus_bool_t
+matchmaker_add_rule (Matchmaker *matchmaker,
+ MatchRule *rule)
+{
+ DBusList **rules;
+
+ _dbus_verbose ("Adding rule with message_type %d, interface %s\n",
+ rule->message_type,
+ rule->interface != NULL ? rule->interface : "<null>");
+
+ rules = matchmaker_get_rules (matchmaker, rule->message_type,
+ rule->interface, TRUE);
+
+ if (rules == NULL)
+ return FALSE;
+
+ if (!_dbus_list_append (rules, rule))
+ return FALSE;
+
+ rule->kdbus_cookie = ++(matchmaker->last_cookie);
+
+ bus_match_rule_ref (rule);
+
+#ifdef DBUS_ENABLE_VERBOSE_MODE
+ {
+ char *s = match_rule_to_string (rule);
+
+ _dbus_verbose ("Added match rule %s to connection %p\n",
+ s, rule->matches_go_to);
+ dbus_free (s);
+ }
+#endif
+
+ return TRUE;
+}
+
+DBusList*
+matchmaker_get_rules_list (Matchmaker *matchmaker,
+ MatchRule *rule)
+{
+ DBusList** list;
+
+ list = matchmaker_get_rules (matchmaker, rule->message_type,
+ rule->interface, FALSE);
+
+ if(list)
+ return *list;
+
+ return NULL;
+}
+
+dbus_bool_t
+match_rule_equal_lib (MatchRule *a,
+ MatchRule *b)
+{
+ if (a->flags != b->flags)
+ return FALSE;
+
+ if (a->matches_go_to != b->matches_go_to)
+ return FALSE;
+
+ if ((a->flags &MATCH_MESSAGE_TYPE) &&
+ a->message_type != b->message_type)
+ return FALSE;
+
+ if ((a->flags &MATCH_MEMBER) &&
+ strcmp (a->member, b->member) != 0)
+ return FALSE;
+
+ if ((a->flags &MATCH_PATH) &&
+ strcmp (a->path, b->path) != 0)
+ return FALSE;
+
+ if ((a->flags &MATCH_INTERFACE) &&
+ strcmp (a->interface, b->interface) != 0)
+ return FALSE;
+
+ if ((a->flags &MATCH_SENDER) &&
+ strcmp (a->sender, b->sender) != 0)
+ return FALSE;
+
+ if ((a->flags &MATCH_DESTINATION) &&
+ strcmp (a->destination, b->destination) != 0)
+ return FALSE;
+
+ /* we already compared the value of flags, and
+ *MATCH_CLIENT_IS_EAVESDROPPING does not have another struct member */
+
+ if (a->flags &MATCH_ARGS)
+ {
+ int i;
+
+ if (a->args_len != b->args_len)
+ return FALSE;
+
+ i = 0;
+ while (i < a->args_len)
+ {
+ int length;
+
+ if ((a->args[i] != NULL) != (b->args[i] != NULL))
+ return FALSE;
+
+ if (a->arg_lens[i] != b->arg_lens[i])
+ return FALSE;
+
+ length = a->arg_lens[i] & ~MATCH_ARG_FLAGS;
+
+ if (a->args[i] != NULL)
+ {
+ _dbus_assert (b->args[i] != NULL);
+ if (memcmp (a->args[i], b->args[i], length) != 0)
+ return FALSE;
+ }
+
+ ++i;
+ }
+ }
+
+ return TRUE;
+}
+
+static void
+bus_matchmaker_remove_rule_link (DBusList **rules,
+ DBusList *link)
+{
+ MatchRule *rule = link->data;
+
+ _dbus_list_remove_link (rules, link);
+
+#ifdef DBUS_ENABLE_VERBOSE_MODE
+ {
+ char *s = match_rule_to_string (rule);
+
+ _dbus_verbose ("Removed match rule %s for connection %p\n",
+ s, rule->matches_go_to);
+ dbus_free (s);
+ }
+#endif
+
+ match_rule_unref (rule);
+}
+
+/* Remove a single rule which is equal to the given rule by value */
+dbus_bool_t
+matchmaker_remove_rule_by_value (Matchmaker *matchmaker,
+ MatchRule *value,
+ DBusError *error)
+{
+ DBusList **rules;
+ DBusList *link = NULL;
+
+ _dbus_verbose ("Removing rule by value with message_type %d, interface %s\n",
+ value->message_type,
+ value->interface != NULL ? value->interface : "<null>");
+
+ rules = matchmaker_get_rules (matchmaker, value->message_type,
+ value->interface, FALSE);
+
+ if (rules != NULL)
+ {
+ /* we traverse backward because bus_connection_remove_match_rule()
+ * removes the most-recently-added rule
+ */
+ link = _dbus_list_get_last_link (rules);
+ while (link != NULL)
+ {
+ MatchRule *rule;
+ DBusList *prev;
+
+ rule = link->data;
+ prev = _dbus_list_get_prev_link (rules, link);
+
+ if (match_rule_equal_lib (rule, value))
+ {
+ bus_matchmaker_remove_rule_link (rules, link);
+ break;
+ }
+
+ link = prev;
+ }
+ }
+
+ if (link == NULL)
+ {
+ dbus_set_error (error, DBUS_ERROR_MATCH_RULE_NOT_FOUND,
+ "The given match rule wasn't found and can't be removed");
+ return FALSE;
+ }
+
+ bus_matchmaker_gc_rules (matchmaker, value->message_type, value->interface,
+ rules);
+
+ return TRUE;
+}
+
+static void
+rule_list_remove (DBusList **rules)
+{
+ DBusList *link;
+
+ link = _dbus_list_get_first_link (rules);
+ while (link != NULL)
+ {
+ DBusList *next;
+
+ next = _dbus_list_get_next_link (rules, link);
+ bus_matchmaker_remove_rule_link (rules, link);
+ link = next;
+ }
+}
+
+void
+free_matchmaker (Matchmaker *matchmaker)
+{
+ int i;
+
+ _dbus_verbose ("Removing all rules for connection\n");
+
+ for (i = DBUS_MESSAGE_TYPE_INVALID; i < DBUS_NUM_MESSAGE_TYPES; i++)
+ {
+ RulePool *p = matchmaker->rules_by_type + i;
+ DBusHashIter iter;
+
+ rule_list_remove (&p->rules_without_iface);
+
+ _dbus_hash_iter_init (p->rules_by_iface, &iter);
+ while (_dbus_hash_iter_next (&iter))
+ {
+ DBusList **items = _dbus_hash_iter_get_value (&iter);
+
+ rule_list_remove (items);
+
+ if (*items == NULL)
+ _dbus_hash_iter_remove_entry (&iter);
+ }
+ }
+}
+
+int
+_match_rule_get_message_type (MatchRule *rule)
+{
+ if (rule->flags & MATCH_MESSAGE_TYPE)
+ return rule->message_type;
+ else
+ return DBUS_MESSAGE_TYPE_INVALID;
+}
+
+const char *
+_match_rule_get_interface (MatchRule *rule)
+{
+ if (rule->flags & MATCH_INTERFACE)
+ return rule->interface;
+ else
+ return NULL;
+}
+
+const char *
+_match_rule_get_member (MatchRule *rule)
+{
+ if (rule->flags & MATCH_MEMBER)
+ return rule->member;
+ else
+ return NULL;
+}
+
+const char *
+_match_rule_get_sender (MatchRule *rule)
+{
+ if (rule->flags & MATCH_SENDER)
+ return rule->sender;
+ else
+ return NULL;
+}
+
+const char *
+_match_rule_get_destination (MatchRule *rule)
+{
+ if (rule->flags & MATCH_DESTINATION)
+ return rule->destination;
+ else
+ return NULL;
+}
+
+const char *
+_match_rule_get_path (MatchRule *rule)
+{
+ if (rule->flags & MATCH_PATH)
+ return rule->path;
+ else
+ return NULL;
+}
+
+const char *
+_match_rule_get_path_namespace (MatchRule *rule)
+{
+ if (rule->flags & MATCH_PATH_NAMESPACE)
+ return rule->path;
+ else
+ return NULL;
+}
+
+int
+_match_rule_get_args_len (MatchRule *rule)
+{
+ return rule->args_len;
+}
+
+const char *
+_match_rule_get_args (MatchRule *rule, int i)
+{
+ return rule->args[i];
+}
+
+unsigned int
+_match_rule_get_arg_lens (MatchRule *rule, int i)
+{
+ return rule->arg_lens[i];
+}
diff --git a/dbus/dbus-signals.h b/dbus/dbus-signals.h
new file mode 100644
index 00000000..da3fd57f
--- /dev/null
+++ b/dbus/dbus-signals.h
@@ -0,0 +1,117 @@
+/*
+ * dbus-signals.h
+ *
+ * Created on: Feb 26, 2014
+ * Author: r.pajak
+ */
+
+#ifndef DBUS_SIGNALS_H_
+#define DBUS_SIGNALS_H_
+
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* signals.h Bus signal connection implementation
+ *
+ * Copyright (C) 2003 Red Hat, Inc.
+ *
+ * Licensed under the Academic Free License version 2.1
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include <dbus/dbus.h>
+#include <dbus/dbus-string.h>
+#include <dbus/dbus-sysdeps.h>
+#include <dbus/dbus-transport.h>
+
+typedef struct Matchmaker Matchmaker;
+typedef struct MatchRule MatchRule;
+
+#ifndef MATCH_ARG_NAMESPACE
+#define MATCH_ARG_NAMESPACE 0x4000000u
+#endif
+#ifndef MATCH_ARG_IS_PATH
+#define MATCH_ARG_IS_PATH 0x8000000u
+#endif
+
+typedef enum
+{
+ MATCH_MESSAGE_TYPE = 1 << 0,
+ MATCH_INTERFACE = 1 << 1,
+ MATCH_MEMBER = 1 << 2,
+ MATCH_SENDER = 1 << 3,
+ MATCH_DESTINATION = 1 << 4,
+ MATCH_PATH = 1 << 5,
+ MATCH_ARGS = 1 << 6,
+ MATCH_PATH_NAMESPACE = 1 << 7,
+ MATCH_CLIENT_IS_EAVESDROPPING = 1 << 8
+} MatchFlags;
+
+void match_rule_unref (MatchRule *rule);
+void match_rule_set_cookie (MatchRule *rule, dbus_uint64_t cookie);
+dbus_uint64_t match_rule_get_cookie (MatchRule *rule);
+
+/* Calling this methods a client declares that it is creating a rule which
+ * needs to eavesdrop (e.g., dbus-monitor), any other created rules not
+ * setting themselves as eavesdropping won't receive any message not addressed
+ * to them, when eavedrop is enabled in the policy. On the other hand, when
+ * eavedrop is not enabled in policy, this method won't have any effect */
+//void bus_match_rule_set_client_is_eavesdropping (BusMatchRule *rule,
+// dbus_bool_t is_eavesdropping);
+
+DBusList ** matchmaker_get_rules (Matchmaker *matchmaker,
+ int message_type,
+ const char *interface,
+ dbus_bool_t create);
+
+MatchRule* match_rule_parse (DBusConnection *matches_go_to,
+ const DBusString *rule_text,
+ DBusError *error);
+
+dbus_bool_t match_rule_equal_lib (MatchRule *a, MatchRule *b);
+
+
+Matchmaker* matchmaker_new (void);
+
+dbus_bool_t matchmaker_add_rule (Matchmaker *matchmaker,
+ MatchRule *rule);
+DBusList* matchmaker_get_rules_list (Matchmaker *matchmaker,
+ MatchRule *rule);
+dbus_bool_t matchmaker_remove_rule_by_value (Matchmaker *matchmaker,
+ MatchRule *value,
+ DBusError *error);
+void free_matchmaker (Matchmaker *matchmaker);
+
+dbus_bool_t match_rule_matches (DBusTransport *transport,
+ MatchRule *rule,
+ DBusMessage *message,
+ MatchFlags already_matched);
+
+#ifdef DBUS_ENABLE_VERBOSE_MODE
+char* match_rule_to_string (MatchRule *rule);
+#endif
+
+int _match_rule_get_message_type (MatchRule *rule);
+const char * _match_rule_get_interface (MatchRule *rule);
+const char * _match_rule_get_member (MatchRule *rule);
+const char * _match_rule_get_sender (MatchRule *rule);
+const char * _match_rule_get_destination (MatchRule *rule);
+const char * _match_rule_get_path (MatchRule *rule);
+const char * _match_rule_get_path_namespace (MatchRule *rule);
+int _match_rule_get_args_len (MatchRule *rule);
+const char * _match_rule_get_args (MatchRule *rule, int i);
+unsigned int _match_rule_get_arg_lens (MatchRule *rule, int i);
+
+#endif /* DBUS_SIGNALS_H_ */
diff --git a/dbus/dbus-transport-kdbus.c b/dbus/dbus-transport-kdbus.c
new file mode 100644
index 00000000..cc819cbb
--- /dev/null
+++ b/dbus/dbus-transport-kdbus.c
@@ -0,0 +1,4000 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-transport-kdbus.c kdbus subclasses of DBusTransport
+ *
+ * Copyright (C) 2002, 2003, 2004, 2006 Red Hat Inc
+ * Copyright (C) 2013 Samsung Electronics
+ *
+ * Licensed under the Academic Free License version 2.1
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version and under the terms of the GNU
+ * Lesser General Public License as published by the
+ * Free Software Foundation; either version 2.1 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+#include "../config.h"
+#include "dbus-transport.h"
+#include "dbus-transport-kdbus.h"
+#include "dbus-transport-protected.h"
+#include "dbus-connection-internal.h"
+#include "dbus-marshal-gvariant.h"
+#include "dbus-asv-util.h"
+#include "kdbus.h"
+#include "dbus-watch.h"
+#include "dbus-errors.h"
+#include "dbus-bus.h"
+#include "kdbus-common.h"
+#include <linux/types.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/syscall.h>
+#include <fcntl.h>
+
+#include <linux/version.h>
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 17, 0)
+# if defined(__arm__) || defined(__aarch64__)
+# define __NR_memfd_create 385
+# elif defined(__i386__)
+# define __NR_memfd_create 279
+# else
+# error "Architecture not supported"
+# endif
+#else
+# include <linux/memfd.h>
+#endif
+
+int debug = -1;
+
+/* FIXME shouldn't it be in fcntl.h header file? copied from systemd's missing.h */
+#ifndef F_LINUX_SPECIFIC_BASE
+#define F_LINUX_SPECIFIC_BASE 1024
+#endif
+
+#ifndef F_ADD_SEALS
+#define F_ADD_SEALS (F_LINUX_SPECIFIC_BASE + 9)
+#define F_GET_SEALS (F_LINUX_SPECIFIC_BASE + 10)
+
+#define F_SEAL_SEAL 0x0001 /* prevent further seals from being set */
+#define F_SEAL_SHRINK 0x0002 /* prevent file from shrinking */
+#define F_SEAL_GROW 0x0004 /* prevent file from growing */
+#define F_SEAL_WRITE 0x0008 /* prevent writes */
+#endif
+
+#ifndef MFD_CLOEXEC
+#define MFD_CLOEXEC 0x0001U
+#endif
+
+#ifndef MFD_ALLOW_SEALING
+#define MFD_ALLOW_SEALING 0x0002U
+#endif
+
+/* ALIGN8 and KDBUS_FOREACH taken from systemd */
+#define ALIGN8(l) (((l) + 7) & ~7)
+#define KDBUS_FOREACH(iter, first, _size) \
+ for (iter = (first); \
+ ((uint8_t *)(iter) < (uint8_t *)(first) + (_size)) && \
+ ((uint8_t *)(iter) >= (uint8_t *)(first)); \
+ iter = (void*)(((uint8_t *)iter) + ALIGN8((iter)->size)))
+#define KDBUS_DEFAULT_TIMEOUT_NS 5000000000LU
+
+/**
+ * @defgroup DBusTransportKdbus DBusTransport implementations for kdbus
+ * @ingroup DBusInternals
+ * @brief Implementation details of DBusTransport on kdbus
+ *
+ * @{
+ */
+
+/** Default Size of the memory area for received non-memfd messages. */
+#define RECEIVE_POOL_SIZE_DEFAULT_SIZE (2 * 1024LU * 1024LU)
+/** Name of environmental variable to define receive pool size*/
+#define RECEIVE_POOL_SIZE_ENV_VAR_NAME "KDBUS_MEMORY_POOL_SIZE"
+/** Max size of pool size in megabytes*/
+#define RECEIVE_POOL_SIZE_MAX_MBYTES 64
+/** Min size of pool size in kilobytes*/
+#define RECEIVE_POOL_SIZE_MIN_KBYTES 16
+
+/** Over this memfd is used to send (if it is not broadcast). */
+#define MEMFD_SIZE_THRESHOLD (512 * 1024LU)
+
+/** Define max bytes read or written in one iteration.
+* This is to avoid blocking on reading or writing for too long. It is checked after each message is sent or received,
+* so if message is bigger than MAX_BYTES_PER_ITERATION it will be handled in one iteration, but sending/writing
+* will break after that message.
+**/
+#define MAX_BYTES_PER_ITERATION 16384
+
+#if (MEMFD_SIZE_THRESHOLD > KDBUS_MSG_MAX_PAYLOAD_VEC_SIZE)
+ #error Memfd size threshold higher than max kdbus message payload vector size
+#endif
+
+/** Enables verbosing more information about kdbus message.
+ * Works only if DBUS_VERBOSE=1 is used.
+ */
+#define KDBUS_MSG_DECODE_DEBUG 0
+
+#define MSG_ITEM_BUILD_VEC(data, datasize) \
+ item->type = KDBUS_ITEM_PAYLOAD_VEC; \
+ item->size = KDBUS_ITEM_HEADER_SIZE + sizeof (struct kdbus_vec); \
+ item->vec.address = (unsigned long) data; \
+ item->vec.size = datasize;
+
+/**
+ * Opaque object representing a transport.
+ */
+typedef struct DBusTransportKdbus DBusTransportKdbus;
+
+/**
+ * Implementation details of DBusTransportKdbus. All members are private.
+ */
+struct DBusTransportKdbus
+{
+ DBusTransport base; /**< Parent instance */
+ kdbus_t *kdbus;
+ DBusWatch *read_watch; /**< Watch for readability. */
+ DBusWatch *write_watch; /**< Watch for writability. */
+
+ int max_bytes_read_per_iteration; /**< To avoid blocking too long. */
+ int max_bytes_written_per_iteration; /**< To avoid blocking too long. */
+
+ char* my_DBus_unique_name; /**< unique name in DBus string format - :1.x , where x is kdbus id*/
+ char* activator; /**< well known name for activator */
+ Matchmaker *matchmaker; /**< for match rules management */
+ dbus_uint32_t client_serial; /**< serial number for messages synthesized by library*/
+};
+
+static kdbus_t *
+get_kdbus (DBusTransport *transport)
+{
+ return ((DBusTransportKdbus*)transport)->kdbus;
+}
+
+/**
+ * Creates unique name string frong unique id.
+ *
+ * @param id unique id
+ * @returns allocated string with unique name
+ *
+ * Caller has to free allocated string with free.
+ */
+static char *
+create_unique_name_from_unique_id (unsigned long long id)
+{
+ char *str = NULL;
+ if (asprintf (&str, ":1.%llu", id) == -1)
+ str = NULL;
+ return str;
+}
+
+/**
+ * Puts locally generated message into received messages queue
+ * @param message message that will be added
+ * @param connection connection to which message will be added
+ * @returns TRUE on success, FALSE on memory allocation error
+ */
+static dbus_bool_t
+add_message_to_received (DBusMessage *message,
+ DBusConnection *connection)
+{
+ DBusList *message_link;
+
+ message_link = _dbus_list_alloc_link (message);
+ if (message_link == NULL)
+ {
+ dbus_message_unref (message);
+ return FALSE;
+ }
+
+ dbus_message_lock (message);
+
+ _dbus_connection_queue_synthesized_message_link (connection, message_link);
+ return TRUE;
+}
+
+static int
+reply_with_error_preset_sender (const char *error_type,
+ const char *template,
+ const char *object,
+ DBusMessage *message,
+ DBusConnection *connection,
+ const char *sender)
+{
+ DBusMessage *errMessage;
+ char* error_msg = "";
+
+ if (template)
+ {
+ error_msg = alloca (strlen (template) + strlen (object) + 1);
+ sprintf (error_msg, template, object);
+ }
+ else if (object)
+ error_msg = (char*)object;
+
+ errMessage = _dbus_generate_local_error_message ( dbus_message_get_serial (message),
+ (char*)error_type,
+ error_msg);
+
+ if (errMessage == NULL)
+ return -1;
+
+ if (sender)
+ dbus_message_set_sender (errMessage, sender);
+
+ if (add_message_to_received (errMessage, connection))
+ return 0;
+
+ return -1;
+}
+
+/**
+ * Generates local error message as a reply to message given as parameter
+ * and adds generated error message to received messages queue.
+ * @param error_type type of error, preferably DBUS_ERROR_(...)
+ * @param template Template of error description. It can has formatting
+ * characters to print object string into it. Can be NULL.
+ * @param object String to print into error description. Can be NULL.
+ * If object is not NULL while template is NULL, the object string
+ * will be the only error description.
+ * @param message Message for which the error reply is generated.
+ * @param connection The connection.
+ * @returns 0 on success, otherwise -1
+ */
+static int
+reply_with_error (const char *error_type,
+ const char *template,
+ const char *object,
+ DBusMessage *message,
+ DBusConnection *connection)
+{
+ return reply_with_error_preset_sender (error_type, template,
+ object, message, connection, NULL);
+}
+
+/**
+ * Generates reply to the message given as a parameter with one item in the reply body
+ * and adds generated reply message to received messages queue.
+ * @param message The message we are replying to.
+ * @param data_type Type of data sent in the reply.Use DBUS_TYPE_(...)
+ * @param pData Address of data sent in the reply.
+ * @param connection The connection
+ * @returns 0 on success, otherwise -1
+ */
+static int
+reply_1_data (DBusMessage *message,
+ int data_type,
+ void *pData,
+ DBusConnection *connection)
+{
+ DBusMessageIter args;
+ DBusMessage *reply;
+
+ reply = dbus_message_new_method_return (message);
+ if (reply == NULL)
+ return -1;
+
+ if (!dbus_message_set_sender (reply, DBUS_SERVICE_DBUS))
+ goto oom_free;
+
+ dbus_message_iter_init_append (reply, &args);
+ if (!dbus_message_iter_append_basic (&args, data_type, pData))
+ goto oom_free;
+
+ if (!add_message_to_received (reply, connection))
+ goto oom_free;
+
+ return 0;
+
+oom_free:
+ dbus_message_unref (reply);
+
+ return -1;
+}
+
+static int
+reply_ack (DBusMessage *message,
+ DBusConnection *connection)
+{
+ DBusMessage *reply;
+ int ret = -1;
+
+ reply = dbus_message_new_method_return (message);
+ if (reply != NULL)
+ {
+ if (add_message_to_received (reply, connection))
+ ret = 0;
+ else
+ dbus_message_unref (reply);
+ }
+ return ret;
+}
+
+static int
+reply_fixed_array (DBusMessage *message,
+ int element_type,
+ const void *data,
+ int n_elements,
+ DBusConnection *connection)
+{
+ DBusMessageIter args, array_iter;
+ DBusMessage *reply;
+
+ reply = dbus_message_new_method_return (message);
+ if (reply == NULL)
+ return -1;
+
+ if (!dbus_message_set_sender (reply, DBUS_SERVICE_DBUS))
+ goto oom_free;
+
+ dbus_message_iter_init_append (reply, &args);
+
+ if (!dbus_message_iter_open_container (&args,
+ DBUS_TYPE_ARRAY,
+ DBUS_TYPE_BYTE_AS_STRING,
+ &array_iter))
+ goto oom_free;
+
+ if (!dbus_message_iter_append_fixed_array (&array_iter, element_type, &data, n_elements))
+ goto oom_array_iter;
+
+ if (!dbus_message_iter_close_container (&args, &array_iter))
+ goto oom_free;
+
+ if (!add_message_to_received (reply, connection))
+ goto oom_free;
+
+ return 0;
+
+oom_array_iter:
+ dbus_message_iter_abandon_container (&args, &array_iter);
+
+oom_free:
+ dbus_message_unref (reply);
+ return -1;
+}
+
+/**
+ * Retrieves file descriptor to memory pool from kdbus module and stores
+ * it in kdbus_transport->memfd. It is then used to send large message.
+ * Triggered when message payload is over MEMFD_SIZE_THRESHOLD
+ * @param kdbus_transport DBusTransportKdbus transport structure
+ * @returns 0 on success, otherwise -1
+ */
+static int
+kdbus_acquire_memfd (DBusTransportKdbus *kdbus_transport,
+ uint64_t fsize)
+{
+ int fd;
+
+ /* FIXME add HAVE_MEMFD_CREATE */
+ if ((fd = syscall (__NR_memfd_create, "kdbus", MFD_ALLOW_SEALING | MFD_CLOEXEC )) < 0)
+ {
+ _dbus_verbose ("memfd_create failed (%d): %m\n", fd);
+ }
+
+ _dbus_verbose ("%s: memfd=%d\n", __FUNCTION__, fd);
+ return fd;
+}
+
+/*
+ * Macros for SipHash algorithm
+ */
+#define ROTL(x,b) (uint64_t)( ((x) << (b)) | ( (x) >> (64 - (b))) )
+
+#define U32TO8_LE(p, v) \
+ (p)[0] = (unsigned char)((v) ); (p)[1] = (unsigned char)((v) >> 8); \
+ (p)[2] = (unsigned char)((v) >> 16); (p)[3] = (unsigned char)((v) >> 24);
+
+#define U64TO8_LE(p, v) \
+ U32TO8_LE((p), (uint32_t)((v) )); \
+ U32TO8_LE((p) + 4, (uint32_t)((v) >> 32));
+
+#define U8TO64_LE(p) \
+ (((uint64_t)((p)[0]) ) | \
+ ((uint64_t)((p)[1]) << 8) | \
+ ((uint64_t)((p)[2]) << 16) | \
+ ((uint64_t)((p)[3]) << 24) | \
+ ((uint64_t)((p)[4]) << 32) | \
+ ((uint64_t)((p)[5]) << 40) | \
+ ((uint64_t)((p)[6]) << 48) | \
+ ((uint64_t)((p)[7]) << 56))
+
+#define SIPROUND \
+ do { \
+ v0 += v1; v1=ROTL(v1,13); v1 ^= v0; v0=ROTL(v0,32); \
+ v2 += v3; v3=ROTL(v3,16); v3 ^= v2; \
+ v0 += v3; v3=ROTL(v3,21); v3 ^= v0; \
+ v2 += v1; v1=ROTL(v1,17); v1 ^= v2; v2=ROTL(v2,32); \
+ } while (0)
+
+
+/*
+ * Hash keys for bloom filters
+ */
+const unsigned char hash_keys[8][16] =
+{
+ {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}
+};
+
+/*
+ * SipHash algorithm
+ */
+static void
+_g_siphash24 (unsigned char out[8],
+ const void *_in,
+ size_t inlen,
+ const unsigned char k[16])
+{
+ uint64_t v0 = 0x736f6d6570736575ULL;
+ uint64_t v1 = 0x646f72616e646f6dULL;
+ uint64_t v2 = 0x6c7967656e657261ULL;
+ uint64_t v3 = 0x7465646279746573ULL;
+ uint64_t b;
+ uint64_t k0 = U8TO64_LE (k);
+ uint64_t k1 = U8TO64_LE (k + 8);
+ uint64_t m;
+ const unsigned char *in = _in;
+ const unsigned char *end = in + inlen - (inlen % sizeof (uint64_t));
+ const int left = inlen & 7;
+ b = ((uint64_t) inlen) << 56;
+ v3 ^= k1;
+ v2 ^= k0;
+ v1 ^= k1;
+ v0 ^= k0;
+
+ for (; in != end; in += 8)
+ {
+ m = U8TO64_LE (in);
+ v3 ^= m;
+ SIPROUND;
+ SIPROUND;
+ v0 ^= m;
+ }
+
+ switch (left)
+ {
+ case 7: b |= ((uint64_t) in[6]) << 48;
+ case 6: b |= ((uint64_t) in[5]) << 40;
+ case 5: b |= ((uint64_t) in[4]) << 32;
+ case 4: b |= ((uint64_t) in[3]) << 24;
+ case 3: b |= ((uint64_t) in[2]) << 16;
+ case 2: b |= ((uint64_t) in[1]) << 8;
+ case 1: b |= ((uint64_t) in[0]); break;
+ case 0: break;
+ }
+
+ v3 ^= b;
+ SIPROUND;
+ SIPROUND;
+ v0 ^= b;
+
+ v2 ^= 0xff;
+ SIPROUND;
+ SIPROUND;
+ SIPROUND;
+ SIPROUND;
+ b = v0 ^ v1 ^ v2 ^ v3;
+ U64TO8_LE (out, b);
+}
+
+static void
+bloom_add_data (uint64_t bloom_data [],
+ struct kdbus_bloom_parameter *bloom_params,
+ const void *data,
+ size_t n)
+{
+ unsigned char hash[8];
+ uint64_t bit_num;
+ unsigned int bytes_num = 0;
+ unsigned int cnt_1, cnt_2;
+ unsigned int hash_index = 0;
+
+ unsigned int c = 0;
+ uint64_t p = 0;
+
+ bit_num = bloom_params->size * 8;
+
+ if (bit_num > 1)
+ bytes_num = ((__builtin_clzll (bit_num) ^ 63U) + 7) / 8;
+
+ for (cnt_1 = 0; cnt_1 < bloom_params->n_hash; cnt_1++)
+ {
+ for (cnt_2 = 0, hash_index = 0; cnt_2 < bytes_num; cnt_2++)
+ {
+ if (c <= 0)
+ {
+ _g_siphash24(hash, data, n, hash_keys[hash_index++]);
+ c += 8;
+ }
+
+ p = (p << 8ULL) | (uint64_t) hash[8 - c];
+ c--;
+ }
+
+ p &= bit_num - 1;
+ bloom_data[p >> 6] |= 1ULL << (p & 63);
+ }
+}
+
+static void
+bloom_add_pair (uint64_t bloom_data [],
+ struct kdbus_bloom_parameter *bloom_params,
+ const char *parameter,
+ const char *value)
+{
+ char buf[1024];
+ size_t size;
+
+ size = strlen (parameter) + strlen (value) + 1;
+ if (size > 1024)
+ return;
+
+ strcpy (stpcpy (stpcpy (buf, parameter), ":"), value);
+ bloom_add_data (bloom_data, bloom_params, buf, size);
+}
+
+static void
+bloom_add_prefixes (uint64_t bloom_data [],
+ struct kdbus_bloom_parameter *bloom_params,
+ const char *parameter,
+ const char *value,
+ char separator)
+{
+ char buf[1024];
+ size_t size;
+
+ size = strlen (parameter) + strlen (value) + 1;
+ if (size > 1024)
+ return;
+
+ strcpy (stpcpy (stpcpy (buf, parameter), ":"), value);
+
+ for (;;)
+ {
+ char *last_sep;
+ last_sep = strrchr (buf, separator);
+ if (!last_sep || last_sep == buf)
+ break;
+
+ *last_sep = 0;
+ bloom_add_data (bloom_data, bloom_params, buf, last_sep-buf);
+ }
+}
+
+static int
+bus_message_setup_bloom (DBusMessage *msg,
+ struct kdbus_bloom_filter *bloom,
+ struct kdbus_bloom_parameter *bloom_params)
+{
+ void *data;
+ unsigned i;
+ const char *str;
+ DBusMessageIter args;
+
+ _dbus_assert (msg);
+ _dbus_assert (bloom);
+
+ data = bloom->data;
+ memset (data, 0, bloom_params->size);
+ bloom->generation = 0;
+
+ bloom_add_pair (data, bloom_params, "message-type",
+ dbus_message_type_to_string (dbus_message_get_type (msg))); //Fixme in systemd type invalid returns NULL but in dbus it returns "invalid"
+
+ str = dbus_message_get_interface (msg);
+ if (str)
+ bloom_add_pair (data, bloom_params, "interface", str);
+ str = dbus_message_get_member (msg);
+ if (str)
+ bloom_add_pair (data, bloom_params, "member", str);
+ str = dbus_message_get_path (msg);
+ if (str)
+ {
+ bloom_add_pair (data, bloom_params, "path", str);
+ bloom_add_pair (data, bloom_params, "path-slash-prefix", str);
+ bloom_add_prefixes (data, bloom_params, "path-slash-prefix", str, '/');
+ }
+
+ if (!dbus_message_iter_init (msg, &args))
+ return 0;
+
+ for (i = 0; i < 64; i++)
+ {
+ char type;
+ char buf[sizeof ("arg")-1 + 2 + sizeof ("-slash-prefix")];
+ char *e;
+
+ type = dbus_message_iter_get_arg_type (&args);
+ if (type != DBUS_TYPE_STRING &&
+ type != DBUS_TYPE_OBJECT_PATH &&
+ type != DBUS_TYPE_SIGNATURE)
+ break;
+
+ dbus_message_iter_get_basic (&args, &str);
+
+ e = stpcpy (buf, "arg");
+ if (i < 10)
+ *(e++) = '0' + (char) i;
+ else {
+ *(e++) = '0' + (char) (i / 10);
+ *(e++) = '0' + (char) (i % 10);
+ }
+
+ *e = 0;
+ bloom_add_pair (data, bloom_params, buf, str);
+
+ strcpy (e, "-dot-prefix");
+ bloom_add_prefixes (data, bloom_params, buf, str, '.');
+ strcpy (e, "-slash-prefix");
+ bloom_add_prefixes (data, bloom_params, buf, str, '/');
+
+ if (!dbus_message_iter_next (&args))
+ break;
+ }
+
+ return 0;
+}
+
+/**
+ * Checks if a string is a unique name or well known name.
+ *
+ * @param name - the string to check
+ * @param id - return pointer for unique id
+ * @returns 1 if the name is unique id, returns 0 if well-known name and -1 on error
+ */
+static int
+parse_name (const char *name,
+ uint64_t *id)
+{
+ char *endptr;
+ /* if name is unique name it must be converted to unique id */
+ if (strncmp (name, ":1.", 3) == 0)
+ {
+ errno = 0;
+ *id = strtoull (&name[3], &endptr, 10);
+ if (*id == 0 || *endptr != '\0' || errno == ERANGE)
+ return -1;
+ else
+ return 1;
+ }
+ else
+ return 0; //well known name
+}
+
+static int
+prepare_mfd (int memfd,
+ const char *header,
+ uint64_t header_size,
+ const char *body,
+ uint64_t body_size)
+{
+ const char *data[] = { header, body };
+ uint64_t count[] = { header_size, body_size };
+ int64_t wr;
+ int p;
+
+ _dbus_verbose ("sending data via memfd\n");
+ for (p = 0; p < sizeof (data) / sizeof (data[0]); ++p)
+ {
+ while (count[p])
+ {
+ wr = write (memfd, data[p], count[p]);
+ if (wr < 0)
+ {
+ _dbus_verbose ("writing to memfd failed: (%d) %m\n", errno);
+ return -1;
+ }
+ count[p] -= wr;
+ data[p] += wr;
+ }
+ }
+
+ // seal data - kdbus module needs it
+ if (fcntl (memfd, F_ADD_SEALS, F_SEAL_SHRINK | F_SEAL_GROW | F_SEAL_WRITE | F_SEAL_SEAL) < 0)
+ {
+ _dbus_verbose ("memfd sealing failed: %d (%m)\n", errno);
+ return -1;
+ }
+ return 0;
+}
+
+static int
+send_message (DBusTransportKdbus *transport,
+ struct kdbus_msg *kdbus_msg,
+ dbus_bool_t sync,
+ const char *destination,
+ dbus_bool_t is_auto_start,
+ struct kdbus_msg **kdbus_msg_reply,
+ DBusError *error)
+{
+ int ret;
+ dbus_uint64_t flags = 0;
+
+ if (sync)
+ flags |= KDBUS_SEND_SYNC_REPLY;
+
+ ret = _kdbus_send (transport->kdbus,
+ flags,
+ kdbus_msg,
+ kdbus_msg_reply);
+ if (ret != 0)
+ {
+ _dbus_verbose ("kdbus error sending message: err %d (%s)\n", ret, _dbus_strerror (ret) );
+
+ switch (ret)
+ {
+ case ENXIO: /* no such id on the bus */
+ case ESRCH:
+ dbus_set_error (error,
+ DBUS_ERROR_NAME_HAS_NO_OWNER,
+ "Name \"%s\" does not exist",
+ destination);
+ break;
+
+ case EADDRNOTAVAIL:
+ case ECONNRESET:
+ /* when well known name is not available on the bus */
+ if (is_auto_start)
+ dbus_set_error (error,
+ DBUS_ERROR_SERVICE_UNKNOWN,
+ "The name %s was not provided by any .service files",
+ destination);
+ else
+ dbus_set_error (error,
+ DBUS_ERROR_NAME_HAS_NO_OWNER,
+ "Name \"%s\" does not exist",
+ destination);
+ break;
+
+ case EMLINK:
+ dbus_set_error (error,
+ DBUS_ERROR_LIMITS_EXCEEDED,
+ NULL,
+ "The maximum number of pending replies per connection has been reached");
+ break;
+
+ case ENOBUFS:
+ case EXFULL:
+ dbus_set_error (error,
+ DBUS_ERROR_LIMITS_EXCEEDED,
+ "No space in receiver's buffer",
+ destination);
+ break;
+
+ default:
+ break;
+ }
+
+ ret = -1;
+ }
+ return ret;
+}
+
+static void
+kdbus_close_message (DBusTransportKdbus *transport, struct kdbus_msg *msg)
+{
+ struct kdbus_item *item;
+
+ KDBUS_ITEM_FOREACH (item, msg, items)
+ {
+ if (item->type == KDBUS_ITEM_PAYLOAD_MEMFD)
+ close (item->memfd.fd);
+ }
+
+ _kdbus_free_mem (transport->kdbus, msg);
+}
+
+static void
+debug_c_str (const char *msg, const char *str, int len)
+{
+ int i;
+ fprintf (stderr, "%s\n", msg);
+ for (i = 0; i < len; i++)
+ {
+ fprintf (stderr, "%02x ", (unsigned char)str[i]);
+ if (i%16==15) fprintf (stderr, "\n");
+ }
+ fprintf (stderr, "\n");
+}
+
+static void
+debug_str (const char *msg, const DBusString *str)
+{
+ debug_c_str (msg, _dbus_string_get_const_data (str), _dbus_string_get_length (str));
+}
+
+static int
+kdbus_write_msg_internal (DBusTransportKdbus *transport,
+ DBusMessage *message,
+ const char *destination,
+ dbus_bool_t check_sync_reply)
+{
+ struct kdbus_msg *msg = NULL;
+ struct kdbus_msg *msg_reply = NULL;
+ struct kdbus_item *item;
+ uint64_t dst_id = KDBUS_DST_ID_BROADCAST;
+ const DBusString *header;
+ const DBusString *body;
+ uint64_t ret_size = -1;
+ uint64_t body_size = 0;
+ uint64_t header_size = 0;
+ int memfd = -1;
+ const int *unix_fds;
+ unsigned fds_count;
+ DBusError error;
+
+ dbus_uint64_t items_size;
+ dbus_uint64_t flags = 0;
+ dbus_uint64_t timeout_ns_or_cookie_reply = 0;
+
+ dbus_error_init (&error);
+
+ // determine destination and destination id
+ if (destination)
+ {
+ dst_id = KDBUS_DST_ID_NAME;
+ switch (parse_name (destination, &dst_id))
+ {
+ case 0: /* well-known name - nothing to do */
+ break;
+ case 1: /* unique name */
+ destination = NULL;
+ break;
+ default: /* error */
+ _dbus_verbose ("error: unique name is not valid: %s\n", destination);
+ return -1;
+ }
+ }
+
+ _dbus_message_get_network_data (message, &header, &body);
+ header_size = _dbus_string_get_length (header);
+ body_size = _dbus_string_get_length (body);
+ ret_size = header_size + body_size;
+
+ /* check whether we can and should use memfd */
+ if ((dst_id != KDBUS_DST_ID_BROADCAST) && (ret_size > MEMFD_SIZE_THRESHOLD))
+ memfd = kdbus_acquire_memfd (transport, ret_size);
+
+ _dbus_message_get_unix_fds (message, &unix_fds, &fds_count);
+
+ items_size = _kdbus_compute_msg_items_size (transport->kdbus,
+ destination,
+ dst_id,
+ body_size,
+ memfd >= 0,
+ fds_count);
+
+ if (!dbus_message_get_auto_start (message))
+ flags |= KDBUS_MSG_NO_AUTO_START;
+
+ if (KDBUS_DST_ID_BROADCAST == dst_id) /* signals */
+ flags |= KDBUS_MSG_SIGNAL;
+ else
+ {
+ if (dbus_message_get_no_reply (message)) /* method replies and errors */
+ timeout_ns_or_cookie_reply = dbus_message_get_reply_serial (message);
+ else /* method calls */
+ {
+ long tv_sec, tv_usec;
+
+ _dbus_get_monotonic_time (&tv_sec, &tv_usec);
+ /* ms us ns */
+ timeout_ns_or_cookie_reply = (dbus_uint64_t)tv_sec * 1000ULL * 1000ULL * 1000ULL
+ + tv_usec * 1000ULL
+ + KDBUS_DEFAULT_TIMEOUT_NS;
+
+ flags |= KDBUS_MSG_EXPECT_REPLY;
+ }
+ }
+
+ msg = _kdbus_new_msg (transport->kdbus,
+ items_size,
+ flags,
+ 0,
+ dst_id,
+ 0,
+ KDBUS_PAYLOAD_DBUS,
+ dbus_message_get_serial (message),
+ timeout_ns_or_cookie_reply);
+ if (NULL == msg)
+ return -1;
+
+ /* build message contents */
+ item = msg->items;
+
+ if (memfd >= 0)
+ {
+ if (prepare_mfd (memfd,
+ _dbus_string_get_const_data (header), header_size,
+ _dbus_string_get_const_data (body), body_size) == -1)
+ {
+ ret_size = -1;
+ goto out;
+ }
+
+ item = _kdbus_item_add_payload_memfd (item,
+ 0,
+ ret_size,
+ memfd);
+ }
+ else
+ {
+ const char* header_data = _dbus_string_get_const_data (header);
+
+ _dbus_verbose ("sending data by vec\n");
+ item = _kdbus_item_add_payload_vec (item,
+ header_size,
+ (dbus_uint64_t)header_data);
+ if (body_size > 0)
+ {
+ const char* body_data = _dbus_string_get_const_data (body);
+
+ if (-1 != debug)
+ {
+ debug_str ("Header to send:", header);
+ debug_str ("Body to send:", body);
+ }
+
+ while (body_size > 0)
+ {
+ dbus_uint64_t part_size = body_size;
+
+ if (part_size > KDBUS_MSG_MAX_PAYLOAD_VEC_SIZE)
+ part_size = KDBUS_MSG_MAX_PAYLOAD_VEC_SIZE;
+
+ _dbus_verbose ("attaching body part\n");
+ item = _kdbus_item_add_payload_vec (item,
+ part_size,
+ (dbus_uint64_t)body_data);
+ body_data += part_size;
+ body_size -= part_size;
+ }
+ }
+ }
+
+ if (fds_count)
+ item = _kdbus_item_add_fds (item, unix_fds, fds_count);
+
+ if (NULL != destination)
+ item = _kdbus_item_add_string (item,
+ KDBUS_ITEM_DST_NAME,
+ destination,
+ strlen (destination) + 1);
+ else if (dst_id == KDBUS_DST_ID_BROADCAST)
+ {
+ struct kdbus_bloom_parameter *bloom = _kdbus_bloom (transport->kdbus);
+ struct kdbus_bloom_filter *filter = NULL;
+ item = _kdbus_item_add_bloom_filter (item,
+ bloom->size,
+ &filter);
+ bus_message_setup_bloom (message, filter, bloom);
+ }
+
+ if (send_message (transport,
+ msg,
+ check_sync_reply,
+ dbus_message_get_destination (message),
+ dbus_message_get_auto_start (message),
+ &msg_reply,
+ &error) != 0)
+ {
+ int ret = -1;
+ if (dbus_error_is_set (&error))
+ ret = reply_with_error (error.name,
+ NULL,
+ error.message,
+ message,
+ transport->base.connection);
+ if (-1 == ret)
+ ret_size = -1;
+ }
+
+ if (check_sync_reply)
+ kdbus_close_message (transport, msg_reply);
+
+ out:
+ if (msg)
+ _kdbus_free_msg (msg);
+ if (memfd >= 0)
+ close (memfd);
+
+ return ret_size;
+}
+
+/**
+ * Sends DBus message using kdbus.
+ * Handles broadcasts and unicast messages, and passing of Unix fds.
+ * Also can locally generate error replies on some error returned by kernel.
+ *
+ * TODO refactor to be more compact - maybe we can send header always as a payload vector
+ * and only message body as memfd if needed.
+ *
+ * @param transport Transport.
+ * @param message DBus message to be sent
+ * @param destination Destination of the message.
+ * @returns bytes sent or -1 if sending failed
+ */
+static int
+kdbus_write_msg (DBusTransportKdbus *transport,
+ DBusMessage *message,
+ const char *destination)
+{
+ return kdbus_write_msg_internal (transport, message, destination, FALSE);
+}
+
+static dbus_uint64_t
+get_pool_size (void)
+{
+ dbus_uint64_t receive_pool_size = RECEIVE_POOL_SIZE_DEFAULT_SIZE;
+ const char *env_pool;
+
+ env_pool = _dbus_getenv (RECEIVE_POOL_SIZE_ENV_VAR_NAME);
+ if (env_pool)
+ {
+ dbus_uint64_t size = 0;
+ unsigned int multiply = 1;
+ long int page_size;
+
+ page_size = sysconf (_SC_PAGESIZE);
+ if (page_size == -1)
+ {
+ goto finish;
+ }
+
+ errno = 0;
+ size = strtoul (env_pool, (char**)&env_pool, 10);
+ if ((errno == EINVAL) || size == 0)
+ {
+ size = 0;
+ goto finish;
+ }
+
+ if (*env_pool == 'k')
+ {
+ multiply = 1024;
+ env_pool++;
+ }
+ else if (*env_pool == 'M')
+ {
+ multiply = 1024 * 1024;
+ env_pool++;
+ }
+
+ if (*env_pool != '\0')
+ {
+ size = 0;
+ goto finish;
+ }
+
+ receive_pool_size = size * multiply;
+
+ if ((receive_pool_size > RECEIVE_POOL_SIZE_MAX_MBYTES * 1024 * 1024) ||
+ (receive_pool_size < RECEIVE_POOL_SIZE_MIN_KBYTES * 1024) ||
+ ((receive_pool_size & (page_size - 1)) != 0)) //pool size must be aligned to page size
+ size = 0;
+
+ finish:
+ if (size == 0)
+ {
+ _dbus_warn ("%s value is invalid, default value %luB will be used.\n", RECEIVE_POOL_SIZE_ENV_VAR_NAME,
+ RECEIVE_POOL_SIZE_DEFAULT_SIZE);
+ _dbus_warn ("Correct value must be between %ukB and %uMB and must be aligned to page size: %ldB.\n",
+ RECEIVE_POOL_SIZE_MIN_KBYTES, RECEIVE_POOL_SIZE_MAX_MBYTES, page_size);
+
+ receive_pool_size = RECEIVE_POOL_SIZE_DEFAULT_SIZE;
+ }
+ }
+
+ _dbus_verbose ("Receive pool size set to %llu.\n", (unsigned long long)receive_pool_size);
+ return receive_pool_size;
+}
+
+/**
+ * Performs kdbus hello - registration on the kdbus bus
+ * needed to send and receive messages on the bus,
+ * and configures transport.
+ * As a result unique id on he bus is obtained.
+ *
+ * @see KDBUS_HELLO_* flags in kdbus.h
+ *
+ * @param transport transport structure
+ * @param registration_flags aditional flags to modify registration process
+ * @returns #TRUE on success
+ */
+static dbus_bool_t
+bus_register_kdbus (DBusTransportKdbus *transport,
+ dbus_uint32_t registration_flags,
+ DBusError *error)
+{
+ int ret;
+ dbus_uint64_t flags;
+
+ flags = KDBUS_HELLO_ACCEPT_FD;
+ if (registration_flags & REGISTER_FLAG_MONITOR)
+ flags |= KDBUS_HELLO_MONITOR;
+
+ ret = _kdbus_hello (transport->kdbus,
+ flags,
+ _KDBUS_ATTACH_ANY,
+ 0,
+ get_pool_size (),
+ transport->activator,
+ "libdbus-kdbus");
+ if (ret != 0)
+ {
+ dbus_set_error (error, DBUS_ERROR_FAILED, "Hello failed: %d", -ret);
+ return FALSE;
+ }
+
+ transport->my_DBus_unique_name = create_unique_name_from_unique_id (_kdbus_id (transport->kdbus));
+ if (NULL == transport->my_DBus_unique_name)
+ {
+ dbus_set_error (error, DBUS_ERROR_NO_MEMORY, "Hello post failed: %d", -ret);
+ return FALSE;
+ }
+
+ _dbus_verbose ("-- Our peer ID is: %llu\n", (unsigned long long)_kdbus_id (transport->kdbus));
+
+ return TRUE;
+}
+
+static dbus_bool_t
+request_DBus_name (DBusTransport *transport,
+ DBusMessage *msg,
+ int *result,
+ DBusError *error)
+{
+ DBusString service_name_real;
+ const DBusString *service_name = &service_name_real;
+ char* name;
+ dbus_uint32_t flags;
+
+ if (!dbus_message_get_args (msg, error,
+ DBUS_TYPE_STRING, &name,
+ DBUS_TYPE_UINT32, &flags,
+ DBUS_TYPE_INVALID))
+ return FALSE;
+
+ _dbus_string_init_const (&service_name_real, name);
+
+ if (!_dbus_validate_bus_name (service_name, 0,
+ _dbus_string_get_length (service_name)))
+ {
+ dbus_set_error (error, DBUS_ERROR_INVALID_ARGS,
+ "Requested bus name \"%s\" is not valid", name);
+
+ _dbus_verbose ("Attempt to acquire invalid service name\n");
+
+ return FALSE;
+ }
+
+ if (_dbus_string_get_byte (service_name, 0) == ':')
+ {
+ /* Not allowed; only base services can start with ':' */
+ dbus_set_error (error, DBUS_ERROR_INVALID_ARGS,
+ "Cannot acquire a service starting with ':' such as \"%s\"", name);
+
+ _dbus_verbose ("Attempt to acquire invalid base service name \"%s\"", name);
+
+ return FALSE;
+ }
+
+ if (_dbus_string_equal_c_str (service_name, DBUS_SERVICE_DBUS))
+ {
+ dbus_set_error (error, DBUS_ERROR_INVALID_ARGS,
+ "Connection is not allowed to own the service \"%s\"because "
+ "it is reserved for D-Bus' use only", DBUS_SERVICE_DBUS);
+ return FALSE;
+ }
+
+ *result = request_kdbus_name (transport, name, flags);
+ if (*result == -EPERM)
+ {
+ dbus_set_error (error, DBUS_ERROR_ACCESS_DENIED,
+ "Kdbus don't allow %s to own the service \"%s\"",
+ ((DBusTransportKdbus*)transport)->my_DBus_unique_name, _dbus_string_get_const_data (service_name));
+ return FALSE;
+ }
+ else if (*result < 0)
+ {
+ dbus_set_error (error, DBUS_ERROR_FAILED , "Name \"%s\" could not be acquired, %d, %m", name, errno);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static dbus_bool_t
+release_DBus_name (DBusTransport *transport,
+ DBusMessage *msg,
+ int *result,
+ DBusError *error)
+{
+ const char *name;
+ DBusString service_name;
+
+ if (!dbus_message_get_args (msg, error,
+ DBUS_TYPE_STRING, &name,
+ DBUS_TYPE_INVALID))
+ return FALSE;
+
+ _dbus_string_init_const (&service_name, name);
+
+ if (!_dbus_validate_bus_name (&service_name, 0,
+ _dbus_string_get_length (&service_name)))
+ {
+ dbus_set_error (error, DBUS_ERROR_INVALID_ARGS,
+ "Given bus name \"%s\" is not valid",
+ _dbus_string_get_const_data (&service_name));
+
+ _dbus_verbose ("Attempt to release invalid service name\n");
+ return FALSE;
+ }
+
+ if (_dbus_string_get_byte (&service_name, 0) == ':')
+ {
+ /* Not allowed; the base service name cannot be created or released */
+ dbus_set_error (error, DBUS_ERROR_INVALID_ARGS,
+ "Cannot release a service starting with ':' such as \"%s\"",
+ _dbus_string_get_const_data (&service_name));
+
+ _dbus_verbose ("Attempt to release invalid base service name \"%s\"",
+ _dbus_string_get_const_data (&service_name));
+ return FALSE;
+ }
+
+ if (_dbus_string_equal_c_str (&service_name, DBUS_SERVICE_DBUS))
+ {
+ /* Not allowed; the base service name cannot be created or released */
+ dbus_set_error (error, DBUS_ERROR_INVALID_ARGS,
+ "Cannot release the %s service because it is owned by the bus",
+ DBUS_SERVICE_DBUS);
+
+ _dbus_verbose ("Attempt to release service name \"%s\"",
+ DBUS_SERVICE_DBUS);
+ return FALSE;
+ }
+
+ *result = release_kdbus_name (transport, name);
+ if (*result < 0)
+ {
+ dbus_set_error (error, DBUS_ERROR_FAILED , "Name \"%s\" could not be released, %d, %m", name, errno);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static int
+strcmp_existing (const char *str1, const char *str2)
+{
+ if (NULL == str1 || NULL == str2)
+ return 0;
+ return strcmp (str1, str2);
+}
+
+static dbus_bool_t
+kernel_match_needed (MatchRule *rule)
+{
+ int message_type;
+
+ /* Allow only NameOwnerChanged member */
+ if (strcmp_existing (_match_rule_get_member (rule), "NameOwnerChanged") != 0)
+ return FALSE;
+
+ /* Allow only signals */
+ message_type = _match_rule_get_message_type (rule);
+ if (message_type != DBUS_MESSAGE_TYPE_INVALID && message_type != DBUS_MESSAGE_TYPE_SIGNAL)
+ return FALSE;
+
+ /* Check if from DBus */
+ if (strcmp_existing (_match_rule_get_sender (rule), DBUS_SERVICE_DBUS) != 0)
+ return FALSE;
+
+ if (strcmp_existing (_match_rule_get_interface (rule), DBUS_INTERFACE_DBUS) != 0)
+ return FALSE;
+
+ if (strcmp_existing (_match_rule_get_path (rule), DBUS_PATH_DBUS) != 0)
+ return FALSE;
+
+ return TRUE;
+}
+
+static dbus_bool_t
+is_bloom_needed (MatchRule *rule)
+{
+ int rule_int;
+ int i;
+
+ if (_match_rule_get_message_type (rule) != DBUS_TYPE_INVALID
+ || _match_rule_get_interface (rule) != NULL
+ || _match_rule_get_member (rule) != NULL
+ || _match_rule_get_path (rule) != NULL
+ || _match_rule_get_path_namespace (rule) != NULL)
+ return TRUE;
+
+ rule_int = _match_rule_get_args_len (rule);
+ for (i = 0; i < rule_int; i++)
+ {
+ if (_match_rule_get_args (rule, i) != NULL)
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static dbus_uint64_t *
+get_bloom (kdbus_t *kdbus, MatchRule *rule)
+{
+ dbus_uint64_t *bloom;
+ dbus_uint64_t bloom_size;
+ int rule_int;
+ const char *rule_string;
+ int i;
+ char argument_buf[sizeof ("arg")-1 + 2 + sizeof ("-slash-prefix") +1];
+
+ bloom_size = _kdbus_bloom (kdbus)->size;
+ bloom = dbus_malloc (bloom_size);
+ if (bloom == NULL)
+ return NULL;
+
+ memset (bloom, 0, bloom_size);
+
+ rule_int = _match_rule_get_message_type (rule);
+ if (rule_int != DBUS_MESSAGE_TYPE_INVALID)
+ {
+ bloom_add_pair (bloom, _kdbus_bloom (kdbus), "message-type", dbus_message_type_to_string (rule_int));
+ _dbus_verbose ("Adding type %s \n", dbus_message_type_to_string (rule_int));
+ }
+
+ rule_string = _match_rule_get_interface (rule);
+ if (rule_string != NULL)
+ {
+ bloom_add_pair (bloom, _kdbus_bloom (kdbus), "interface", rule_string);
+ _dbus_verbose ("Adding interface %s \n", rule_string);
+ }
+
+ rule_string = _match_rule_get_member (rule);
+ if (rule_string != NULL)
+ {
+ bloom_add_pair (bloom, _kdbus_bloom (kdbus), "member", rule_string);
+ _dbus_verbose ("Adding member %s \n", rule_string);
+ }
+
+ rule_string = _match_rule_get_path (rule);
+ if (rule_string != NULL)
+ {
+ bloom_add_pair (bloom, _kdbus_bloom (kdbus), "path", rule_string);
+ _dbus_verbose ("Adding path %s \n", rule_string);
+ }
+
+ rule_string = _match_rule_get_path_namespace (rule);
+ if (rule_string != NULL)
+ {
+ bloom_add_pair (bloom, _kdbus_bloom (kdbus), "path-slash-prefix", rule_string);
+ _dbus_verbose ("Adding path-slash-prefix %s \n", rule_string);
+ }
+
+ rule_int = _match_rule_get_args_len (rule);
+ for (i = 0; i < rule_int; i++)
+ {
+ rule_string = _match_rule_get_args (rule, i);
+ if (rule_string != NULL)
+ {
+ unsigned int rule_arg_lens = _match_rule_get_arg_lens (rule, i);
+ if (rule_arg_lens & MATCH_ARG_IS_PATH)
+ {
+ sprintf (argument_buf, "arg%d-slash-prefix", i);
+ bloom_add_prefixes (bloom, _kdbus_bloom (kdbus), argument_buf, rule_string, '/');
+ }
+ else if (rule_arg_lens & MATCH_ARG_NAMESPACE)
+ {
+ sprintf (argument_buf, "arg%d-dot-prefix", i);
+ bloom_add_prefixes (bloom, _kdbus_bloom (kdbus), argument_buf, rule_string, '.');
+ }
+ else
+ {
+ sprintf (argument_buf, "arg%d", i);
+ bloom_add_pair (bloom, _kdbus_bloom (kdbus), argument_buf, rule_string);
+ }
+ }
+ }
+
+ return bloom;
+}
+
+/**
+ * Adds a match rule to match broadcast messages going through the message bus.
+ * Do no affect messages addressed directly.
+ *
+ * copied a lot from systemd bus_add_match_internal_kernel ()
+ *
+ * TODO add error reporting
+ *
+ * @param transport transport
+ * @param match rule
+ */
+static dbus_bool_t
+add_match_kdbus (DBusTransportKdbus *transport,
+ MatchRule *rule)
+{
+ struct kdbus_cmd_match *cmd;
+ struct kdbus_item *item;
+ int sender = -1;
+ int sender_size = 0;
+ __u64 bloom_size;
+ __u64 rule_cookie;
+ uint64_t src_id = KDBUS_MATCH_ID_ANY;
+ uint64_t items_size;
+ uint64_t *bloom;
+ dbus_bool_t need_bloom = FALSE;
+
+ const char *rule_sender;
+ int ret;
+
+ rule_cookie = match_rule_get_cookie (rule);
+
+/*
+ * First check if it is org.freedesktop.DBus's NameOwnerChanged or any
+ * org.freedesktop.DBus combination that includes this,
+ * because it must be converted to special kdbus rule (kdbus has separate rules
+ * for kdbus (kernel) generated broadcasts).
+ */
+ if (kernel_match_needed (rule))
+ {
+ ret = _kdbus_add_match_name_change (transport->kdbus,
+ 0,
+ rule_cookie,
+ KDBUS_MATCH_ID_ANY,
+ 0,
+ KDBUS_MATCH_ID_ANY,
+ 0);
+ if (0 != ret)
+ {
+ _dbus_verbose ("Failed adding match rule for name removal for daemon, error: %d, %s\n",
+ ret, _dbus_strerror (ret));
+ return FALSE;
+ }
+
+ ret = _kdbus_add_match_id_change (transport->kdbus,
+ 0,
+ rule_cookie,
+ KDBUS_MATCH_ID_ANY,
+ 0);
+ if (0 != ret)
+ {
+ _dbus_verbose ("Failed adding match rule for adding id for daemon, error: %d, %s\n",
+ ret, _dbus_strerror (ret));
+ return FALSE;
+ }
+
+ _dbus_verbose ("Added match rule for kernel correctly.\n");
+
+/*
+ * In case all of sender, interface and path are NULL, the rule
+ * says simply about NameHasOwner signal from any object, any interface, any sender.
+ * So, we need to consider that.
+ * Otherwise, our job is finished here.
+ */
+ if (_match_rule_get_sender (rule) != NULL
+ || _match_rule_get_interface (rule) != NULL
+ || _match_rule_get_path (rule) != NULL)
+ return TRUE;
+ }
+
+/*
+ * standard rule - registered in general way, for non-kernel broadcasts
+ * kdbus doesn't use it to check kdbus (kernel) generated broadcasts
+ */
+
+ items_size = 0;
+
+ need_bloom = is_bloom_needed (rule);
+ if (need_bloom)
+ {
+ bloom_size = _kdbus_bloom (transport->kdbus)->size;
+ items_size += KDBUS_ITEM_SIZE (bloom_size);
+ bloom = get_bloom (transport->kdbus, rule);
+ if (NULL == bloom)
+ return FALSE;
+ }
+
+ rule_sender = _match_rule_get_sender (rule);
+ if (rule_sender != NULL)
+ {
+ sender = parse_name (rule_sender, &src_id);
+ if (sender < 0)
+ return FALSE;
+
+ if (sender > 0) /* unique_id */
+ items_size += KDBUS_ITEM_SIZE (sizeof (uint64_t));
+ else /* well-known name */
+ {
+ sender_size = strlen (rule_sender) + 1;
+ items_size += KDBUS_ITEM_SIZE (sender_size);
+ }
+ }
+
+ cmd = _kdbus_new_cmd_match (transport->kdbus,
+ items_size,
+ 0,
+ rule_cookie);
+ if (NULL == cmd)
+ ret = ENOMEM;
+ else
+ {
+ item = cmd->items;
+ if (0 == sender) /* well-known name */
+ {
+ item = _kdbus_item_add_string (item,
+ KDBUS_ITEM_NAME,
+ rule_sender,
+ sender_size);
+ _dbus_verbose ("Adding sender %s \n", rule_sender);
+ }
+ else if (KDBUS_MATCH_ID_ANY != src_id) /* unique id */
+ {
+ item = _kdbus_item_add_id (item, src_id);
+ _dbus_verbose ("Adding src_id %llu \n", (unsigned long long)src_id);
+ }
+
+ if (need_bloom)
+ {
+ item = _kdbus_item_add_bloom_mask (item, bloom, bloom_size);
+ dbus_free (bloom);
+ }
+
+ ret = _kdbus_add_match (transport->kdbus, cmd);
+
+ _kdbus_free_cmd_match (cmd);
+ }
+
+ if (0 != ret)
+ {
+ _dbus_verbose ("Failed adding match bus rule cookie %llu,\nerror: %d, %s\n",
+ rule_cookie, ret, _dbus_strerror (ret));
+ return FALSE;
+ }
+
+ _dbus_verbose ("Added match bus rule %llu\n", rule_cookie);
+ return TRUE;
+}
+
+static int
+capture_org_freedesktop_DBus_Hello (DBusTransportKdbus *transport,
+ DBusMessage *message,
+ DBusError *error)
+{
+ DBusMessageIter args;
+ dbus_uint32_t registration_flags = 0;
+
+ dbus_message_iter_init (message, &args);
+ if (dbus_message_iter_get_arg_type (&args) == DBUS_TYPE_UINT32)
+ dbus_message_iter_get_basic (&args, &registration_flags);
+
+ if (!bus_register_kdbus (transport, registration_flags, error))
+ goto out;
+
+ if (!reply_1_data (message, DBUS_TYPE_STRING, &transport->my_DBus_unique_name, transport->base.connection))
+ return 0; /* on success we can not free name */
+
+out:
+ free (transport->my_DBus_unique_name);
+ return -1;
+}
+
+static int
+capture_org_freedesktop_DBus_RequestName (DBusTransportKdbus *transport,
+ DBusMessage *message,
+ DBusError *error)
+{
+ int result;
+
+ if (!request_DBus_name (&transport->base, message, &result, error))
+ return -1;
+
+ return reply_1_data (message, DBUS_TYPE_UINT32, &result,
+ transport->base.connection);
+}
+
+static int
+capture_org_freedesktop_DBus_ReleaseName (DBusTransportKdbus *transport,
+ DBusMessage *message,
+ DBusError *error)
+{
+ int result;
+
+ if (!release_DBus_name (&transport->base, message, &result, error))
+ return -1;
+
+ return reply_1_data (message, DBUS_TYPE_UINT32, &result,
+ transport->base.connection);
+}
+
+static int
+capture_org_freedesktop_DBus_AddMatch (DBusTransportKdbus *transport,
+ DBusMessage *message,
+ DBusError *error)
+{
+ const char *arg;
+ DBusString arg_str;
+ MatchRule *rule = NULL;
+ DBusConnection *connection = transport->base.connection;
+
+ if (!dbus_message_get_args (message, error,
+ DBUS_TYPE_STRING, &arg,
+ DBUS_TYPE_INVALID))
+ goto failed;
+
+ _dbus_string_init_const (&arg_str, arg);
+
+ rule = match_rule_parse (connection, &arg_str, error);
+ if (rule == NULL)
+ goto failed;
+
+ if (!matchmaker_add_rule (transport->matchmaker, rule))
+ {
+ dbus_set_error_const (error, DBUS_ERROR_NO_MEMORY, "No memory to store match rule");
+ goto failed;
+ }
+
+ if (!add_match_kdbus (transport, rule))
+ {
+ dbus_set_error (error, _dbus_error_from_errno (errno), "Could not add match rule, %s",
+ _dbus_strerror_from_errno ());
+ goto failed;
+ }
+
+ match_rule_unref (rule);
+ return reply_ack (message, connection);
+
+failed:
+ if (rule)
+ match_rule_unref (rule);
+ _dbus_verbose ("Error during AddMatch in lib: %s, %s\n", error->name, error->message);
+ return -1;
+}
+
+static int
+capture_org_freedesktop_DBus_RemoveMatch (DBusTransportKdbus *transport,
+ DBusMessage *message,
+ DBusError *error)
+{
+ const char *arg;
+ DBusString arg_str;
+ MatchRule *rule = NULL;
+ DBusConnection *connection = transport->base.connection;
+
+ if (!dbus_message_get_args (message, error,
+ DBUS_TYPE_STRING, &arg,
+ DBUS_TYPE_INVALID))
+ goto failed_remove;
+
+ _dbus_string_init_const (&arg_str, arg);
+
+ rule = match_rule_parse (connection, &arg_str, error);
+ if (rule == NULL)
+ goto failed_remove;
+
+ if (!kdbus_remove_match (&transport->base, matchmaker_get_rules_list (transport->matchmaker, rule),
+ transport->my_DBus_unique_name, rule, error))
+ goto failed_remove;
+
+ if (!matchmaker_remove_rule_by_value (transport->matchmaker, rule, error))
+ goto failed_remove;
+
+ match_rule_unref (rule);
+ return reply_ack (message, connection);
+
+failed_remove:
+ if (rule)
+ match_rule_unref (rule);
+ _dbus_verbose ("Error during RemoveMatch in lib: %s, %s\n", error->name, error->message);
+ return -1;
+}
+
+static int
+get_connection_info_by_name (DBusMessage *message,
+ DBusError *error,
+ struct nameInfo *info,
+ DBusTransport *transport,
+ const char *name,
+ dbus_bool_t getLabel)
+{
+ int ret;
+ if (!dbus_validate_bus_name (name, error))
+ return -1;
+
+ if ((ret = _kdbus_connection_info_by_name (get_kdbus (transport), name, getLabel, info)) != 0)
+ {
+ if (ESRCH == ret || ENXIO == ret)
+ dbus_set_error (error, DBUS_ERROR_NAME_HAS_NO_OWNER,
+ "Could not get owner of name '%s': no such name", name);
+ else
+ dbus_set_error (error, DBUS_ERROR_FAILED,
+ "Unable to query name %s, returned %d, errno = %d (%m).",
+ name, ret, errno);
+ return -1;
+ }
+ if (info->flags & KDBUS_HELLO_ACTIVATOR)
+ {
+ dbus_set_error (error, DBUS_ERROR_NAME_HAS_NO_OWNER,
+ "Could not get owner of name '%s'", name);
+ /* we return ESRCH - this is an indicator that name has an activator */
+ return -ESRCH;
+ }
+ return 0;
+}
+
+/* This local function handles common case for org.freedesktop.DBus method handlers:
+ * 1. gets string argument from incoming message;
+ * 2. gets connection info for such name.
+ * Note: if getLabel argument is set to TRUE, the caller must free info.sec_label.
+ */
+static int
+get_connection_info_from_message_argument (DBusMessage *message,
+ DBusError *error,
+ struct nameInfo *info,
+ DBusTransport *transport,
+ dbus_bool_t getLabel)
+{
+ const char *arg;
+
+ if (!dbus_message_get_args (message, error,
+ DBUS_TYPE_STRING, &arg,
+ DBUS_TYPE_INVALID))
+ return -1;
+
+ return get_connection_info_by_name (message, error, info, transport, arg, getLabel);
+}
+
+static int
+capture_org_freedesktop_DBus_GetConnectionCredentials (DBusTransportKdbus *transport,
+ DBusMessage *message,
+ DBusError *error)
+{
+ DBusMessage *reply = NULL;
+ DBusConnection *conn = transport->base.connection;
+ DBusMessageIter reply_iter;
+ DBusMessageIter array_iter;
+ struct nameInfo info;
+
+ if (get_connection_info_from_message_argument (message, error, &info, &transport->base, TRUE) != 0)
+ return -1;
+
+ reply = _dbus_asv_new_method_return (message, &reply_iter, &array_iter);
+ if (reply == NULL)
+ return -1;
+
+ /* we can't represent > 32-bit pids; if your system needs them, please
+ * add ProcessID64 to the spec or something */
+ if (info.processId <= _DBUS_UINT32_MAX)
+ {
+ if (!_dbus_asv_add_uint32 (&array_iter, "ProcessID", info.processId))
+ goto oom;
+ }
+ /* we can't represent > 32-bit uids; if your system needs them, please
+ * add UnixUserID64 to the spec or something */
+ if (info.userId <= _DBUS_UINT32_MAX)
+ {
+ if (!_dbus_asv_add_uint32 (&array_iter, "UnixUserID", info.userId))
+ goto oom;
+ }
+
+ if (info.sec_label != NULL)
+ {
+ dbus_bool_t res = _dbus_asv_add_array_of_bytes (&array_iter, "LinuxSecurityLabel",
+ info.sec_label,
+ strlen (info.sec_label)+1);
+
+ dbus_free (info.sec_label);
+
+ if (!res)
+ goto oom;
+ }
+
+ if (!_dbus_asv_close (&reply_iter, &array_iter))
+ goto oom;
+
+ if (!dbus_message_set_sender (reply, DBUS_SERVICE_DBUS))
+ goto oom;
+
+ if (!add_message_to_received (reply, conn))
+ goto oom;
+
+ return 0;
+
+oom:
+ _dbus_asv_abandon (&reply_iter, &array_iter);
+ dbus_message_unref (reply);
+
+ return -1;
+}
+
+static int
+capture_org_freedesktop_DBus_GetConnectionSELinuxSecurityContext (DBusTransportKdbus *transport,
+ DBusMessage *message,
+ DBusError *error)
+{
+ struct nameInfo info;
+
+ if (get_connection_info_from_message_argument (message, error, &info, &transport->base, TRUE) != 0)
+ return -1;
+
+ if (info.sec_label != NULL)
+ {
+ int ret = reply_fixed_array (message, DBUS_TYPE_BYTE,
+ info.sec_label,
+ strlen (info.sec_label)+1,
+ transport->base.connection);
+
+ dbus_free (info.sec_label);
+ return ret;
+ }
+ else
+ {
+ dbus_set_error (error, DBUS_ERROR_NOT_SUPPORTED, "Operation not supported");
+ }
+
+ return -1;
+}
+
+static int
+capture_org_freedesktop_DBus_GetConnectionUnixProcessID (DBusTransportKdbus *transport,
+ DBusMessage *message,
+ DBusError *error)
+{
+ struct nameInfo info;
+ dbus_uint32_t processId;
+
+ if (get_connection_info_from_message_argument (message, error, &info, &transport->base, FALSE) != 0 ||
+ info.processId > _DBUS_UINT32_MAX)
+ return -1;
+
+ processId = info.processId;
+
+ return reply_1_data (message, DBUS_TYPE_UINT32, &processId, transport->base.connection);
+}
+
+static int
+capture_org_freedesktop_DBus_GetConnectionUnixUser (DBusTransportKdbus *transport,
+ DBusMessage *message,
+ DBusError *error)
+{
+ struct nameInfo info;
+ dbus_uint32_t userId;
+
+ if (get_connection_info_from_message_argument (message, error, &info, &transport->base, FALSE) != 0 ||
+ info.userId > _DBUS_UINT32_MAX)
+ return -1;
+
+ userId = info.userId;
+
+ return reply_1_data (message, DBUS_TYPE_UINT32, &userId, transport->base.connection);
+}
+
+static int
+capture_org_freedesktop_DBus_GetId (DBusTransportKdbus *transport,
+ DBusMessage *message,
+ DBusError *error)
+{
+ dbus_uint64_t bus_id_size = _kdbus_bus_id_size ();
+ char bus_id[bus_id_size*2+1];
+ char *bus_id_ptr = bus_id;
+ char *bus_id_original = _kdbus_bus_id (transport->kdbus);
+ int i = 0;
+ for (; i < bus_id_size; i++)
+ sprintf (bus_id + 2*i, "%02x", bus_id_original[i]);
+ return reply_1_data (message, DBUS_TYPE_STRING, &bus_id_ptr, transport->base.connection);
+}
+
+static int
+capture_org_freedesktop_DBus_GetNameOwner (DBusTransportKdbus *transport,
+ DBusMessage *message,
+ DBusError *error)
+{
+ struct nameInfo info;
+ char *unique_name;
+ int ret;
+ const char *arg;
+
+ if (!dbus_message_get_args (message, error,
+ DBUS_TYPE_STRING, &arg,
+ DBUS_TYPE_INVALID))
+ return -1;
+
+ if (strcmp (arg, DBUS_SERVICE_DBUS) == 0)
+ {
+ if (-1 == asprintf (&unique_name, "%s", DBUS_SERVICE_DBUS))
+ return -1;
+ }
+ else
+ {
+ if (get_connection_info_by_name (message, error, &info, &transport->base, arg, FALSE) != 0)
+ return -1;
+
+ unique_name = create_unique_name_from_unique_id (info.uniqueId);
+ if (NULL == unique_name)
+ return -1;
+ }
+
+ ret = reply_1_data (message, DBUS_TYPE_STRING, &unique_name, transport->base.connection);
+
+ free (unique_name);
+
+ return ret;
+}
+
+static int
+reply_listNames (DBusTransportKdbus *transport,
+ DBusMessage *message,
+ DBusError *error,
+ dbus_uint64_t flags)
+{
+ DBusMessage *reply = NULL;
+ dbus_uint64_t prev_id = 0;
+
+ /* First, get the list from kdbus */
+
+ struct kdbus_info *name_list, *name;
+ dbus_uint64_t list_size;
+ int ret;
+ DBusMessageIter iter;
+ DBusMessageIter array_iter;
+
+ ret = _kdbus_list (transport->kdbus,
+ flags,
+ &name_list,
+ &list_size);
+ if (ret != 0)
+ {
+ dbus_set_error (error, DBUS_ERROR_FAILED, "Error listing names");
+ return -1;
+ }
+
+ /* Compose the reply on the fly */
+ reply = dbus_message_new_method_return (message);
+ if (reply == NULL)
+ goto oom;
+
+ dbus_message_iter_init_append (reply, &iter);
+ if (!dbus_message_iter_open_container (&iter,
+ DBUS_TYPE_ARRAY,
+ DBUS_TYPE_STRING_AS_STRING,
+ &array_iter))
+ goto oom_reply;
+
+ KDBUS_FOREACH (name, name_list, list_size)
+ {
+ struct kdbus_item *item;
+
+ if ((flags & KDBUS_LIST_UNIQUE) && name->id != prev_id)
+ {
+ dbus_bool_t res;
+
+ char *unique_name = create_unique_name_from_unique_id (name->id);
+
+ if (NULL == unique_name)
+ goto oom_iterator;
+
+ res = dbus_message_iter_append_basic (&array_iter,
+ DBUS_TYPE_STRING,
+ &unique_name);
+ free (unique_name);
+
+ if (!res)
+ goto oom_iterator;
+ }
+
+ KDBUS_ITEM_FOREACH (item, name, items)
+ {
+ if (item->type == KDBUS_ITEM_OWNED_NAME)
+ {
+ DBusError local_error;
+ char *name_ptr = item->name.name;
+
+ dbus_error_init ( &local_error );
+ if (!dbus_validate_bus_name (name_ptr, &local_error))
+ continue;
+
+ if (flags & KDBUS_LIST_QUEUED)
+ name_ptr = create_unique_name_from_unique_id (name->id);
+
+ if (NULL == name_ptr)
+ goto oom_iterator;
+
+ if (!dbus_message_iter_append_basic (&array_iter,
+ DBUS_TYPE_STRING,
+ &name_ptr))
+ goto oom_iterator;
+
+ if (flags & KDBUS_LIST_QUEUED)
+ free (name_ptr);
+ }
+ }
+ }
+
+ if (!dbus_message_iter_close_container (&iter, &array_iter))
+ goto oom_reply;
+
+ if (!dbus_message_set_sender (reply, DBUS_SERVICE_DBUS))
+ goto oom_reply;
+
+ /* Finally, send the reply */
+ if (!add_message_to_received (reply, transport->base.connection))
+ goto oom_reply;
+
+ return 0;
+
+oom_iterator:
+ dbus_message_iter_abandon_container (&iter, &array_iter);
+
+oom_reply:
+ dbus_message_unref (reply);
+oom:
+ _kdbus_free_mem (transport->kdbus, name_list);
+ return -1;
+}
+
+static int
+capture_org_freedesktop_DBus_ListActivatableNames (DBusTransportKdbus *transport,
+ DBusMessage *message,
+ DBusError *error)
+{
+ return reply_listNames (transport, message, error, KDBUS_LIST_ACTIVATORS);
+}
+
+static int
+capture_org_freedesktop_DBus_ListNames (DBusTransportKdbus *transport,
+ DBusMessage *message,
+ DBusError *error)
+{
+ return reply_listNames (transport, message, error, KDBUS_LIST_UNIQUE | KDBUS_LIST_NAMES);
+}
+
+static int
+capture_org_freedesktop_DBus_ListQueuedOwners (DBusTransportKdbus *transport,
+ DBusMessage *message,
+ DBusError *error)
+{
+ struct nameInfo info;
+
+ if (get_connection_info_from_message_argument (message, error, &info, &transport->base, FALSE) != 0)
+ return -1;
+
+ return reply_listNames (transport, message, error, KDBUS_LIST_QUEUED);
+}
+
+static int
+capture_org_freedesktop_DBus_NameHasOwner (DBusTransportKdbus *transport,
+ DBusMessage *message,
+ DBusError *error)
+{
+ struct nameInfo info;
+ dbus_bool_t result = TRUE;
+
+ if (get_connection_info_from_message_argument (message, error, &info, &transport->base, FALSE) != 0)
+ {
+ if (dbus_error_is_set (error) && dbus_error_has_name (error, DBUS_ERROR_NAME_HAS_NO_OWNER))
+ {
+ result = FALSE;
+ dbus_error_free (error);
+ }
+ else
+ return -1;
+ }
+
+ return reply_1_data (message, DBUS_TYPE_BOOLEAN, &result, transport->base.connection);
+}
+
+static int
+capture_org_freedesktop_DBus_ReloadConfig (DBusTransportKdbus *transport,
+ DBusMessage *message,
+ DBusError *error)
+{
+ DBusMessageIter iter;
+ DBusMessage *reply = NULL;
+
+ dbus_message_iter_init (message, &iter);
+ if (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_STRUCT)
+ {
+ dbus_set_error (error, DBUS_ERROR_INVALID_ARGS,
+ "Call to 'ReloadConfig' has wrong args");
+ return -1;
+ }
+
+ reply = dbus_message_new_method_return (message);
+ if (reply == NULL)
+ return -1;
+
+ if (!dbus_message_set_sender (reply, DBUS_SERVICE_DBUS))
+ goto oom;
+
+ if (!add_message_to_received (reply, transport->base.connection))
+ goto oom;
+
+ return 0;
+
+oom:
+ dbus_message_unref (reply);
+ return -1;
+}
+
+static int
+capture_org_freedesktop_DBus_StartServiceByName (DBusTransportKdbus *transport,
+ DBusMessage *message,
+ DBusError *error)
+{
+ struct nameInfo info;
+ char *name;
+ dbus_uint32_t flags; /* Spec says: not used, but we check the syntax anyway */
+ int ret = 0;
+ dbus_bool_t dbus_service = FALSE;
+
+ if (!dbus_message_get_args (message, error,
+ DBUS_TYPE_STRING, &name,
+ DBUS_TYPE_UINT32, &flags,
+ DBUS_TYPE_INVALID))
+ return -1;
+
+ dbus_service = (strncmp (name, DBUS_SERVICE_DBUS, strlen (DBUS_SERVICE_DBUS) + 1) == 0);
+
+ if (!dbus_service)
+ ret = get_connection_info_by_name (message,
+ error,
+ &info,
+ &transport->base,
+ name,
+ FALSE);
+
+ if (dbus_service || 0 == ret)
+ {
+ dbus_uint32_t status = DBUS_START_REPLY_ALREADY_RUNNING;
+ return reply_1_data (message, DBUS_TYPE_UINT32, &status, transport->base.connection);
+ }
+ else if (-ESRCH == ret) /* there is an activator */
+ {
+ DBusMessage *sub_message;
+
+ /* if we are here, then we have error set - free place for possible real error */
+ dbus_error_free (error);
+
+ /* send method call to org.freedesktop.DBus.Peer.Ping */
+ sub_message = dbus_message_new_method_call (name, "/", DBUS_INTERFACE_PEER, "Ping");
+ if (sub_message == NULL)
+ return -1;
+
+ /* The serial number here is set to -1. A message needs a valid serial number.
+ * We do not have access to connection's serial numbers counter, so we need to make up one.
+ * -1 is the last valid serial, so we hope that we'll never get there, especially with 64-bit
+ * kdbus cookies.
+ */
+ dbus_message_set_serial (sub_message, -1);
+
+ dbus_message_lock (sub_message);
+
+ if (kdbus_write_msg_internal (transport, sub_message, name, FALSE) == -1)
+ return -1;
+ else
+ {
+ dbus_uint32_t status = DBUS_START_REPLY_SUCCESS;
+ return reply_1_data (message, DBUS_TYPE_UINT32, &status, transport->base.connection);
+ }
+ }
+ else
+ {
+ /*
+ * There was an error set in get_connection_info_by_name()
+ * We want to have another error return from StartServiceByName.
+ */
+ dbus_error_free (error);
+ dbus_set_error (error,
+ DBUS_ERROR_SERVICE_UNKNOWN,
+ "The name %s was not provided by any .service files",
+ name);
+ }
+
+ return -1;
+}
+
+static int
+capture_org_freedesktop_DBus_UpdateActivationEnvironment (DBusTransportKdbus *transport,
+ DBusMessage *message,
+ DBusError *error)
+{
+ dbus_set_error (error, DBUS_ERROR_NOT_SUPPORTED,
+ "'%s' method not supported", dbus_message_get_member (message));
+ return -1;
+}
+
+typedef int (*CaptureHandler)(DBusTransportKdbus *, DBusMessage *, DBusError *);
+struct CaptureHandlers {
+ const char *method_name;
+ CaptureHandler handler;
+};
+
+#define HANDLER_ELEMENT(x) {#x, capture_org_freedesktop_DBus_##x}
+
+/* This is to cut the code to parts, and keep it organized:
+ * an array of elements of type, as in example:
+ * { "RequestName", capture_org_freedesktop_DBus_RequestName }
+ * That is, a method of name RequestName will be handled by capture_org_freedesktop_DBus_RequestName ().
+ */
+static struct CaptureHandlers capture_handlers[] =
+{
+// "Hello" is handled separately
+// HANDLER_ELEMENT(Hello),
+ HANDLER_ELEMENT (RequestName),
+ HANDLER_ELEMENT (ReleaseName),
+ HANDLER_ELEMENT (AddMatch),
+ HANDLER_ELEMENT (RemoveMatch),
+ HANDLER_ELEMENT (GetConnectionCredentials),
+ HANDLER_ELEMENT (GetConnectionSELinuxSecurityContext),
+ HANDLER_ELEMENT (GetConnectionUnixProcessID),
+ HANDLER_ELEMENT (GetConnectionUnixUser),
+ HANDLER_ELEMENT (GetId),
+ HANDLER_ELEMENT (GetNameOwner),
+ HANDLER_ELEMENT (ListActivatableNames),
+ HANDLER_ELEMENT (ListNames),
+ HANDLER_ELEMENT (ListQueuedOwners),
+ HANDLER_ELEMENT (NameHasOwner),
+ HANDLER_ELEMENT (ReloadConfig),
+ HANDLER_ELEMENT (StartServiceByName),
+ HANDLER_ELEMENT (UpdateActivationEnvironment)
+};
+
+/**
+ * Looks over messages sent to org.freedesktop.DBus. Hello message, which performs
+ * registration on the bus, is captured as it must be locally converted into
+ * appropriate ioctl. AddMatch and RemoveMatch are captured to store match rules
+ * locally in case of false positive result of kdbus bloom filters, but after
+ * being read they are passed to org.freedesktop.DBus to register these rules
+ * in kdbus.
+ * All the rest org.freedesktop.DBus methods are left untouched
+ * and they are sent to dbus-daemon in the same way as every other messages.
+ *
+ * @param transport Transport
+ * @param message Message being sent.
+ * @returns 1 if message is not captured and should be passed to daemon
+ * 0 if message was handled locally and correctly (it includes proper return of error reply),
+ * -1 message to org.freedesktop.DBus was not handled correctly.
+ */
+static int
+capture_org_freedesktop_DBus (DBusTransportKdbus *transport,
+ const char *destination,
+ DBusMessage *message)
+{
+ int ret = 1;
+ if (!strcmp (destination, DBUS_SERVICE_DBUS))
+ {
+ if (!strcmp (dbus_message_get_interface (message), DBUS_INTERFACE_DBUS))
+ {
+ DBusError error;
+ const char *member = dbus_message_get_member (message);
+
+ dbus_error_init (&error);
+
+ ret = -1;
+ if (!strcmp (member, "Hello"))
+ {
+ ret = capture_org_freedesktop_DBus_Hello (transport, message, &error);
+ }
+ else
+ {
+ int i = 0;
+ int handlers_size = sizeof (capture_handlers)/sizeof (capture_handlers[0]);
+
+ while (i < handlers_size && strcmp (member, capture_handlers[i].method_name) != 0)
+ i++;
+
+ if (i < handlers_size)
+ {
+ ret = capture_handlers[i].handler (transport, message, &error);
+ }
+ else
+ {
+ dbus_set_error (&error, DBUS_ERROR_UNKNOWN_METHOD,
+ "org.freedesktop.DBus does not understand message %s", member);
+ }
+ }
+
+ if (ret != 0 && dbus_error_is_set (&error))
+ {
+ ret = reply_with_error ((char*)error.name, NULL, error.message, message,
+ transport->base.connection);
+ dbus_error_free (&error);
+ }
+ }
+ }
+
+ return ret; //send message to daemon
+}
+
+#if KDBUS_MSG_DECODE_DEBUG == 1
+static const char
+*msg_id (uint64_t id)
+{
+ char buf[64];
+ const char* const_ptr;
+
+ if (id == 0)
+ return "KERNEL";
+ if (id == ~0ULL)
+ return "BROADCAST";
+
+ sprintf (buf, "%llu", (unsigned long long)id);
+
+ const_ptr = buf;
+ return const_ptr;
+}
+#endif
+struct kdbus_enum_table {
+ long long id;
+ const char *name;
+};
+#define _STRINGIFY(x) #x
+#define STRINGIFY(x) _STRINGIFY(x)
+#define ELEMENTSOF(x) (sizeof (x)/sizeof ((x)[0]))
+#define TABLE(what) static struct kdbus_enum_table kdbus_table_##what[]
+#define ENUM(_id) { .id=_id, .name=STRINGIFY(_id) }
+#define LOOKUP(what) \
+ const char *enum_##what (long long id) { \
+ size_t i; \
+ for (i = 0; i < ELEMENTSOF(kdbus_table_##what); i++) \
+ if (id == kdbus_table_##what[i].id) \
+ return kdbus_table_##what[i].name; \
+ return "UNKNOWN"; \
+ }
+const char *enum_MSG(long long id);
+TABLE(MSG) = {
+ ENUM(_KDBUS_ITEM_NULL),
+ ENUM(KDBUS_ITEM_PAYLOAD_VEC),
+ ENUM(KDBUS_ITEM_PAYLOAD_OFF),
+ ENUM(KDBUS_ITEM_PAYLOAD_MEMFD),
+ ENUM(KDBUS_ITEM_FDS),
+ ENUM(KDBUS_ITEM_BLOOM_PARAMETER),
+ ENUM(KDBUS_ITEM_BLOOM_FILTER),
+ ENUM(KDBUS_ITEM_DST_NAME),
+ ENUM(KDBUS_ITEM_CREDS),
+ ENUM(KDBUS_ITEM_PID_COMM),
+ ENUM(KDBUS_ITEM_TID_COMM),
+ ENUM(KDBUS_ITEM_EXE),
+ ENUM(KDBUS_ITEM_CMDLINE),
+ ENUM(KDBUS_ITEM_CGROUP),
+ ENUM(KDBUS_ITEM_CAPS),
+ ENUM(KDBUS_ITEM_SECLABEL),
+ ENUM(KDBUS_ITEM_AUDIT),
+ ENUM(KDBUS_ITEM_CONN_DESCRIPTION),
+ ENUM(KDBUS_ITEM_NAME),
+ ENUM(KDBUS_ITEM_TIMESTAMP),
+ ENUM(KDBUS_ITEM_NAME_ADD),
+ ENUM(KDBUS_ITEM_NAME_REMOVE),
+ ENUM(KDBUS_ITEM_NAME_CHANGE),
+ ENUM(KDBUS_ITEM_ID_ADD),
+ ENUM(KDBUS_ITEM_ID_REMOVE),
+ ENUM(KDBUS_ITEM_REPLY_TIMEOUT),
+ ENUM(KDBUS_ITEM_REPLY_DEAD),
+};
+LOOKUP(MSG);
+const char *enum_PAYLOAD(long long id);
+TABLE(PAYLOAD) = {
+ ENUM(KDBUS_PAYLOAD_KERNEL),
+ ENUM(KDBUS_PAYLOAD_DBUS),
+};
+LOOKUP(PAYLOAD);
+
+static dbus_uint32_t
+get_next_client_serial (DBusTransportKdbus *transport)
+{
+ dbus_uint32_t serial;
+
+ serial = transport->client_serial++;
+
+ if (transport->client_serial == 0)
+ transport->client_serial = 1;
+
+ return serial;
+}
+
+/**
+ * Calculates length of the kdbus message content (payload).
+ *
+ * @param msg kdbus message
+ * @return the length of the kdbus message's payload.
+ */
+static int
+kdbus_message_size (const struct kdbus_msg* msg)
+{
+ const struct kdbus_item *item;
+ int ret_size = 0;
+
+ KDBUS_ITEM_FOREACH(item, msg, items)
+ {
+ if (item->size < KDBUS_ITEM_HEADER_SIZE)
+ {
+ _dbus_verbose (" +%s (%llu bytes) invalid data record\n", enum_MSG(item->type), item->size);
+ return -1;
+ }
+ switch (item->type)
+ {
+ case KDBUS_ITEM_PAYLOAD_OFF:
+ ret_size += item->vec.size;
+ break;
+ case KDBUS_ITEM_PAYLOAD_MEMFD:
+ ret_size += item->memfd.size;
+ break;
+ default:
+ break;
+ }
+ }
+
+ return ret_size;
+}
+
+static int
+generate_NameSignal (const char *signal,
+ const char *name,
+ DBusTransportKdbus *transport)
+{
+ DBusMessage *message;
+
+ _dbus_verbose ("Generating %s for %s.\n", signal, name);
+
+ message = dbus_message_new_signal (DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS, signal);
+ if (message == NULL)
+ return -1;
+
+ if (!dbus_message_append_args (message, DBUS_TYPE_STRING, &name, DBUS_TYPE_INVALID))
+ goto error;
+ if (!dbus_message_set_destination (message, transport->my_DBus_unique_name))
+ goto error;
+ if (!dbus_message_set_sender (message, DBUS_SERVICE_DBUS))
+ goto error;
+ dbus_message_set_serial (message, get_next_client_serial (transport));
+
+ if (!add_message_to_received (message, transport->base.connection))
+ return -1;
+
+ return 0;
+
+ error:
+ dbus_message_unref (message);
+ return -1;
+}
+
+/*
+ * The NameOwnerChanged signals take three parameters with
+ * unique or well-known names, but only some forms actually
+ * exist:
+ *
+ * WELLKNOWN, "", UNIQUE → KDBUS_ITEM_NAME_ADD
+ * WELLKNOWN, UNIQUE, "" → KDBUS_ITEM_NAME_REMOVE
+ * WELLKNOWN, UNIQUE, UNIQUE → KDBUS_ITEM_NAME_CHANGE
+ * UNIQUE, "", UNIQUE → KDBUS_ITEM_ID_ADD
+ * UNIQUE, UNIQUE, "" → KDBUS_ITEM_ID_REMOVE
+ *
+ * For the latter two the two unique names must be identical.
+ */
+static int
+kdbus_handle_name_owner_changed (__u64 type,
+ const char *bus_name,
+ __u64 old,
+ __u64 new,
+ DBusTransportKdbus *transport)
+{
+ DBusMessage *message = NULL;
+ DBusMessageIter args;
+ char tmp_str[128];
+ const char *const_ptr;
+
+ if ((message = dbus_message_new_signal (DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS, "NameOwnerChanged")) == NULL)
+ return -1;
+
+ dbus_message_iter_init_append (message, &args);
+
+ // for ID_ADD and ID_REMOVE this function takes NULL as bus_name
+ if (bus_name == NULL)
+ {
+ sprintf (tmp_str,":1.%llu", old != 0 ? old : new);
+ const_ptr = tmp_str;
+ }
+ else
+ const_ptr = bus_name;
+
+ if (!dbus_message_iter_append_basic (&args, DBUS_TYPE_STRING, &const_ptr))
+ goto error;
+
+ _dbus_verbose ("%s\n", const_ptr);
+
+
+ if ((old==0) && (new==0))
+ {
+ /* kdbus generates its own set of events that can not be passed to
+ * client without translation. */
+ const char *src = "org.freedesktop.DBus";
+ const char *dst = "org.freedesktop.DBus";
+
+ if (type == KDBUS_ITEM_NAME_ADD || type == KDBUS_ITEM_ID_ADD)
+ src = "";
+ else if (type == KDBUS_ITEM_NAME_REMOVE || type == KDBUS_ITEM_ID_REMOVE)
+ dst = "";
+
+ if (!dbus_message_iter_append_basic (&args, DBUS_TYPE_STRING, &src))
+ goto error;
+ if (!dbus_message_iter_append_basic (&args, DBUS_TYPE_STRING, &dst))
+ goto error;
+
+ _dbus_verbose ("[NameOwnerChanged:%s, old=%lld, new=%lld\n", __func__, old, new);
+ }
+ else
+ {
+ // determine and append old_id
+ if (old != 0)
+ {
+ sprintf (tmp_str,":1.%llu", old);
+ const_ptr = tmp_str;
+ }
+ else
+ const_ptr = "";
+
+ if (!dbus_message_iter_append_basic (&args, DBUS_TYPE_STRING, &const_ptr))
+ goto error;
+
+ _dbus_verbose ("%s\n", const_ptr);
+ // determine and append new_id
+ if (new != 0)
+ {
+ sprintf (tmp_str,":1.%llu", new);
+ const_ptr = tmp_str;
+ }
+ else
+ const_ptr = "";
+
+ if (!dbus_message_iter_append_basic (&args, DBUS_TYPE_STRING, &const_ptr))
+ goto error;
+
+ _dbus_verbose ("%s\n", const_ptr);
+ }
+
+ dbus_message_set_sender (message, DBUS_SERVICE_DBUS);
+ dbus_message_set_serial (message, get_next_client_serial (transport));
+
+ if (!add_message_to_received (message, transport->base.connection))
+ return -1;
+
+ return 0;
+
+error:
+ dbus_message_unref (message);
+
+ return -1;
+}
+
+static void
+_handle_item_timestamp (const struct kdbus_item *item)
+{
+#if KDBUS_MSG_DECODE_DEBUG == 1
+ _dbus_verbose (" +%s (%llu bytes) realtime=%lluns monotonic=%lluns\n",
+ enum_MSG(item->type), item->size,
+ (unsigned long long)item->timestamp.realtime_ns,
+ (unsigned long long)item->timestamp.monotonic_ns);
+#endif
+}
+
+static void
+_handle_unexpected_item (const struct kdbus_item *item)
+{
+ _dbus_assert_not_reached ("unexpected item from kdbus");
+}
+
+static void
+_handle_padding (const struct kdbus_msg *msg,
+ const struct kdbus_item *end_of_items)
+{
+#if KDBUS_MSG_DECODE_DEBUG == 1
+ if ((char *)end_of_items - ((char *)msg + msg->size) >= 8)
+ _dbus_verbose ("invalid padding at end of message\n");
+#endif
+}
+
+static int
+kdbus_decode_dbus_message (const struct kdbus_msg *msg,
+ char *data,
+ DBusTransportKdbus *kdbus_transport,
+ int *fds,
+ int *n_fds)
+{
+ const struct kdbus_item *item;
+ int ret_size = 0;
+
+ *n_fds = 0;
+
+ KDBUS_ITEM_FOREACH(item, msg, items)
+ {
+ if (item->size < KDBUS_ITEM_HEADER_SIZE)
+ {
+ _dbus_verbose (" +%s (%llu bytes) invalid data record\n", enum_MSG(item->type), item->size);
+ ret_size = -1;
+ break;
+ }
+
+ switch (item->type)
+ {
+ case KDBUS_ITEM_PAYLOAD_OFF:
+ memcpy (data, (char *)msg+item->vec.offset, item->vec.size);
+ data += item->vec.size;
+ ret_size += item->vec.size;
+
+ if (-1 != debug)
+ {
+ debug_c_str ("Message part arrived:", (char *)msg+item->vec.offset, item->vec.size);
+ }
+
+ _dbus_verbose (" +%s (%llu bytes) off=%llu size=%llu\n",
+ enum_MSG(item->type), item->size,
+ (unsigned long long)item->vec.offset,
+ (unsigned long long)item->vec.size);
+ break;
+
+ case KDBUS_ITEM_PAYLOAD_MEMFD:
+ {
+ char *buf;
+ uint64_t size;
+
+ size = item->memfd.size;
+ _dbus_verbose ("memfd.size : %llu\n", (unsigned long long)size);
+
+ buf = mmap (NULL, size, PROT_READ , MAP_SHARED, item->memfd.fd, 0);
+ if (buf == MAP_FAILED)
+ {
+ _dbus_verbose ("mmap () fd=%i failed:%m", item->memfd.fd);
+ return -1;
+ }
+
+ memcpy (data, buf, size);
+ data += size;
+ ret_size += size;
+
+ munmap (buf, size);
+ close (item->memfd.fd);
+
+ _dbus_verbose (" +%s (%llu bytes) off=%llu size=%llu\n",
+ enum_MSG(item->type), item->size,
+ (unsigned long long)item->vec.offset,
+ (unsigned long long)item->vec.size);
+ }
+ break;
+
+ case KDBUS_ITEM_FDS:
+ {
+ int i;
+
+ *n_fds = (item->size - KDBUS_ITEM_HEADER_SIZE) / sizeof (int);
+ memcpy (fds, item->fds, *n_fds * sizeof (int));
+ for (i = 0; i < *n_fds; i++)
+ _dbus_fd_set_close_on_exec (fds[i]);
+ }
+ break;
+
+ case KDBUS_ITEM_CREDS:
+#if KDBUS_MSG_DECODE_DEBUG == 1
+ _dbus_verbose (" +%s (%llu bytes) uid=%lld, gid=%lld, pid=%lld, tid=%lld, starttime=%lld\n",
+ enum_MSG(item->type), item->size,
+ item->creds.uid, item->creds.gid,
+ item->creds.pid, item->creds.tid,
+ item->creds.starttime);
+#endif
+ break;
+
+ case KDBUS_ITEM_PID_COMM:
+ case KDBUS_ITEM_TID_COMM:
+ case KDBUS_ITEM_EXE:
+ case KDBUS_ITEM_CGROUP:
+ case KDBUS_ITEM_SECLABEL:
+ case KDBUS_ITEM_DST_NAME:
+#if KDBUS_MSG_DECODE_DEBUG == 1
+ _dbus_verbose (" +%s (%llu bytes) '%s' (%zu)\n",
+ enum_MSG(item->type), item->size, item->str, strlen (item->str));
+#endif
+ break;
+
+ case KDBUS_ITEM_CMDLINE:
+ case KDBUS_ITEM_NAME:
+#if KDBUS_MSG_DECODE_DEBUG == 1
+ {
+ __u64 size = item->size - KDBUS_ITEM_HEADER_SIZE;
+ const char *str = item->str;
+ int count = 0;
+
+ _dbus_verbose (" +%s (%llu bytes) ", enum_MSG(item->type), item->size);
+ while (size)
+ {
+ _dbus_verbose ("'%s' ", str);
+ size -= strlen (str) + 1;
+ str += strlen (str) + 1;
+ count++;
+ }
+
+ _dbus_verbose ("(%d string%s)\n", count, (count == 1) ? "" : "s");
+ }
+#endif
+ break;
+
+ case KDBUS_ITEM_AUDIT:
+#if KDBUS_MSG_DECODE_DEBUG == 1
+ _dbus_verbose (" +%s (%llu bytes) loginuid=%llu sessionid=%llu\n",
+ enum_MSG(item->type), item->size,
+ (unsigned long long)item->data64[0],
+ (unsigned long long)item->data64[1]);
+#endif
+ break;
+
+ case KDBUS_ITEM_CAPS:
+#if KDBUS_MSG_DECODE_DEBUG == 1
+ {
+ int n;
+ const uint32_t *cap;
+ int i;
+
+ _dbus_verbose (" +%s (%llu bytes) len=%llu bytes)\n",
+ enum_MSG(item->type), item->size,
+ (unsigned long long)item->size - KDBUS_ITEM_HEADER_SIZE);
+
+ cap = item->data32;
+ n = (item->size - KDBUS_ITEM_HEADER_SIZE) / 4 / sizeof (uint32_t);
+
+ _dbus_verbose (" CapInh=");
+ for (i = 0; i < n; i++)
+ _dbus_verbose ("%08x", cap[(0 * n) + (n - i - 1)]);
+
+ _dbus_verbose (" CapPrm=");
+ for (i = 0; i < n; i++)
+ _dbus_verbose ("%08x", cap[(1 * n) + (n - i - 1)]);
+
+ _dbus_verbose (" CapEff=");
+ for (i = 0; i < n; i++)
+ _dbus_verbose ("%08x", cap[(2 * n) + (n - i - 1)]);
+
+ _dbus_verbose (" CapInh=");
+ for (i = 0; i < n; i++)
+ _dbus_verbose ("%08x", cap[(3 * n) + (n - i - 1)]);
+ _dbus_verbose ("\n");
+ }
+#endif
+ break;
+
+ case KDBUS_ITEM_TIMESTAMP:
+ _handle_item_timestamp (item);
+ break;
+
+ case KDBUS_ITEM_BLOOM_FILTER:
+ /* no handling */
+ break;
+
+ default:
+ _handle_unexpected_item (item);
+ break;
+ }
+ }
+
+ _handle_padding (msg, item);
+
+ return ret_size;
+}
+
+static int
+kdbus_decode_kernel_message (const struct kdbus_msg *msg,
+ DBusTransportKdbus *kdbus_transport)
+{
+ const struct kdbus_item *item;
+ int ret_size = 0;
+
+ KDBUS_ITEM_FOREACH (item, msg, items)
+ {
+ if (item->size < KDBUS_ITEM_HEADER_SIZE)
+ {
+ _dbus_verbose (" +%s (%llu bytes) invalid data record\n", enum_MSG (item->type), item->size);
+ ret_size = -1;
+ break;
+ }
+
+ switch (item->type)
+ {
+ case KDBUS_ITEM_REPLY_TIMEOUT:
+ case KDBUS_ITEM_REPLY_DEAD:
+ {
+ DBusMessage *message = NULL;
+ _dbus_verbose (" +%s (%llu bytes) cookie=%llu\n",
+ enum_MSG (item->type), item->size, msg->cookie_reply);
+
+ message = _dbus_generate_local_error_message (msg->cookie_reply,
+ item->type == KDBUS_ITEM_REPLY_TIMEOUT ? DBUS_ERROR_NO_REPLY : DBUS_ERROR_NAME_HAS_NO_OWNER, NULL);
+ if (message == NULL)
+ {
+ ret_size = -1;
+ goto out;
+ }
+
+ dbus_message_set_serial (message, get_next_client_serial (kdbus_transport));
+
+ if (!add_message_to_received (message, kdbus_transport->base.connection))
+ ret_size = -1;
+ }
+ break;
+
+ case KDBUS_ITEM_NAME_ADD:
+ case KDBUS_ITEM_NAME_REMOVE:
+ case KDBUS_ITEM_NAME_CHANGE:
+ {
+ int local_ret;
+
+ _dbus_verbose (" +%s (%llu bytes) '%s', old id=%lld, new id=%lld, old flags=0x%llx, new flags=0x%llx\n",
+ enum_MSG(item->type), (unsigned long long) item->size,
+ item->name_change.name, item->name_change.old_id.id,
+ item->name_change.new_id.id, item->name_change.old_id.flags,
+ item->name_change.new_id.flags);
+
+ if (item->name_change.new_id.id == _kdbus_id (kdbus_transport->kdbus))
+ ret_size = generate_NameSignal ("NameAcquired", item->name_change.name, kdbus_transport);
+ else if (item->name_change.old_id.id == _kdbus_id (kdbus_transport->kdbus))
+ ret_size = generate_NameSignal ("NameLost", item->name_change.name, kdbus_transport);
+
+ if (ret_size == -1)
+ goto out;
+
+ if (item->name_change.new_id.flags & KDBUS_NAME_ACTIVATOR)
+ local_ret = kdbus_handle_name_owner_changed (item->type,
+ item->name_change.name,
+ item->name_change.old_id.id, 0,
+ kdbus_transport);
+ else if (item->name_change.old_id.flags & KDBUS_NAME_ACTIVATOR)
+ local_ret = kdbus_handle_name_owner_changed (item->type,
+ item->name_change.name, 0,
+ item->name_change.new_id.id,
+ kdbus_transport);
+ else
+ local_ret = kdbus_handle_name_owner_changed (item->type,
+ item->name_change.name,
+ item->name_change.old_id.id,
+ item->name_change.new_id.id,
+ kdbus_transport);
+ if (local_ret == -1)
+ goto out;
+
+ ret_size += local_ret;
+ }
+ break;
+
+ case KDBUS_ITEM_ID_ADD:
+ case KDBUS_ITEM_ID_REMOVE:
+ _dbus_verbose (" +%s (%llu bytes) id=%llu flags=%llu\n",
+ enum_MSG(item->type), (unsigned long long) item->size,
+ (unsigned long long) item->id_change.id,
+ (unsigned long long) item->id_change.flags);
+
+ if (item->id_change.flags & KDBUS_HELLO_ACTIVATOR)
+ ret_size = kdbus_handle_name_owner_changed (item->type, NULL, 0, 0,
+ kdbus_transport);
+ else
+ ret_size = kdbus_handle_name_owner_changed (item->type, NULL,
+ item->type == KDBUS_ITEM_ID_ADD ? 0 : item->id_change.id,
+ item->type == KDBUS_ITEM_ID_ADD ? item->id_change.id : 0,
+ kdbus_transport);
+
+ if (ret_size == -1)
+ goto out;
+ break;
+
+ case KDBUS_ITEM_TIMESTAMP:
+ _handle_item_timestamp (item);
+ break;
+
+ default:
+ _handle_unexpected_item (item);
+ break;
+ }
+ }
+
+ _handle_padding (msg, item);
+
+out:
+ return ret_size;
+}
+
+/**
+ * Decodes kdbus message in order to extract DBus message and puts it into received data buffer
+ * and file descriptor's buffer. Also captures kdbus error messages and kdbus kernel broadcasts
+ * and converts all of them into appropriate DBus messages.
+ *
+ * @param msg kdbus message
+ * @param data place to copy DBus message to
+ * @param kdbus_transport transport
+ * @param fds place to store file descriptors received
+ * @param n_fds place to store quantity of file descriptors received
+ * @return number of DBus message's bytes received or -1 on error
+ */
+static int
+kdbus_decode_msg (const struct kdbus_msg *msg,
+ char *data,
+ DBusTransportKdbus *kdbus_transport,
+ int *fds,
+ int *n_fds)
+{
+ int ret_size = 0;
+
+#if KDBUS_MSG_DECODE_DEBUG == 1
+ _dbus_verbose ("MESSAGE: %s (%llu bytes) flags=0x%llx, %s → %s, cookie=%llu, timeout=%llu\n",
+ enum_PAYLOAD(msg->payload_type),
+ (unsigned long long) msg->size,
+ (unsigned long long) msg->flags,
+ msg_id (msg->src_id),
+ msg_id (msg->dst_id),
+ (unsigned long long) msg->cookie,
+ (unsigned long long) msg->timeout_ns);
+#endif
+
+ switch (msg->payload_type)
+ {
+ case KDBUS_PAYLOAD_DBUS:
+ ret_size = kdbus_decode_dbus_message (msg, data, kdbus_transport, fds, n_fds);
+ break;
+ case KDBUS_PAYLOAD_KERNEL:
+ ret_size = kdbus_decode_kernel_message (msg, kdbus_transport);
+ break;
+ default:
+ _dbus_assert_not_reached ("unexpected payload type from kdbus");
+ break;
+ }
+
+ return ret_size;
+}
+
+/**
+ * Reads message from kdbus and puts it into DBus buffers
+ *
+ * @param kdbus_transport transport
+ * @param buffer place to copy received message to
+ * @param fds place to store file descriptors received with the message
+ * @param n_fds place to store quantity of file descriptors received
+ * @return size of received message on success, -1 on error
+ */
+static int
+kdbus_read_message (DBusTransportKdbus *kdbus_transport,
+ DBusString *buffer,
+ int *fds,
+ int *n_fds)
+{
+ int ret_size, buf_size;
+ struct kdbus_msg *msg;
+ char *data;
+ int start;
+ dbus_uint64_t flags = 0;
+ int ret;
+
+ start = _dbus_string_get_length (buffer);
+
+ if (kdbus_transport->activator != NULL)
+ flags |= KDBUS_RECV_PEEK;
+
+ ret = _kdbus_recv (kdbus_transport->kdbus, flags, 0, &msg);
+
+ if (0 != ret)
+ {
+ _dbus_verbose ("kdbus error receiving message: %d (%s)\n", ret, _dbus_strerror (ret));
+ _dbus_string_set_length (buffer, start);
+ return -1;
+ }
+
+ buf_size = kdbus_message_size (msg);
+ if (buf_size == -1)
+ {
+ _dbus_verbose ("kdbus error - too short message: %d (%m)\n", errno);
+ return -1;
+ }
+
+ /* What is the maximum size of the locally generated message?
+ I just assume 2048 bytes */
+ buf_size = MAX(buf_size, 2048);
+
+ if (!_dbus_string_lengthen (buffer, buf_size))
+ {
+ errno = ENOMEM;
+ return -1;
+ }
+ data = _dbus_string_get_data_len (buffer, start, buf_size);
+
+ ret_size = kdbus_decode_msg (msg, data, kdbus_transport, fds, n_fds);
+
+ if (ret_size == -1) /* error */
+ {
+ _dbus_string_set_length (buffer, start);
+ return -1;
+ }
+ else if (buf_size != ret_size) /* case of locally generated message */
+ {
+ _dbus_string_set_length (buffer, start + ret_size);
+ }
+
+ _dbus_message_loader_set_unique_sender_id (kdbus_transport->base.loader, msg->src_id);
+
+ if (kdbus_transport->activator != NULL)
+ return ret_size;
+
+ ret = _kdbus_free_mem (kdbus_transport->kdbus, msg);
+ if (0 != ret)
+ {
+ _dbus_verbose ("kdbus error freeing message: %d (%s)\n", ret, _dbus_strerror (ret));
+ return -1;
+ }
+
+ return ret_size;
+}
+
+/**
+ * Copy-paste from socket transport. Only renames done.
+ */
+static void
+free_watches (DBusTransport *transport)
+{
+ DBusTransportKdbus *kdbus_transport = (DBusTransportKdbus*) transport;
+
+ _dbus_verbose ("start\n");
+
+ if (kdbus_transport->read_watch)
+ {
+ if (transport->connection)
+ _dbus_connection_remove_watch_unlocked (transport->connection,
+ kdbus_transport->read_watch);
+ _dbus_watch_invalidate (kdbus_transport->read_watch);
+ _dbus_watch_unref (kdbus_transport->read_watch);
+ kdbus_transport->read_watch = NULL;
+ }
+
+ if (kdbus_transport->write_watch)
+ {
+ if (transport->connection)
+ _dbus_connection_remove_watch_unlocked (transport->connection,
+ kdbus_transport->write_watch);
+ _dbus_watch_invalidate (kdbus_transport->write_watch);
+ _dbus_watch_unref (kdbus_transport->write_watch);
+ kdbus_transport->write_watch = NULL;
+ }
+
+ _dbus_verbose ("end\n");
+}
+
+/**
+ * Copy-paste from socket transport. Only done needed renames and removed
+ * lines related to encoded messages.
+ */
+static void
+transport_finalize (DBusTransport *transport)
+{
+ DBusTransportKdbus *kdbus_transport = (DBusTransportKdbus *)transport;
+ _dbus_verbose ("\n");
+
+ free_watches (transport);
+
+ _dbus_transport_finalize_base (transport);
+
+ _dbus_assert (kdbus_transport->read_watch == NULL);
+ _dbus_assert (kdbus_transport->write_watch == NULL);
+
+ free_matchmaker (kdbus_transport->matchmaker);
+
+ dbus_free (kdbus_transport->activator);
+
+ _kdbus_free (kdbus_transport->kdbus);
+
+ dbus_free (transport);
+}
+
+/**
+ * Copy-paste from socket transport. Removed code related to authentication,
+ * socket_transport replaced by kdbus_transport.
+ */
+static void
+check_write_watch (DBusTransport *transport)
+{
+ DBusTransportKdbus *kdbus_transport = (DBusTransportKdbus*) transport;
+ dbus_bool_t needed;
+
+ if (transport->connection == NULL)
+ return;
+
+ if (transport->disconnected)
+ {
+ _dbus_assert (kdbus_transport->write_watch == NULL);
+ return;
+ }
+
+ _dbus_transport_ref (transport);
+
+ needed = _dbus_connection_has_messages_to_send_unlocked (transport->connection);
+
+ _dbus_verbose ("check_write_watch (): needed = %d on connection %p watch %p fd = %d outgoing messages exist %d\n",
+ needed, transport->connection, kdbus_transport->write_watch,
+ _kdbus_fd (kdbus_transport->kdbus),
+ _dbus_connection_has_messages_to_send_unlocked (transport->connection));
+
+ _dbus_connection_toggle_watch_unlocked (transport->connection,
+ kdbus_transport->write_watch,
+ needed);
+
+ _dbus_transport_unref (transport);
+}
+
+/**
+ * Copy-paste from socket transport. Removed code related to authentication,
+ * socket_transport replaced by kdbus_transport.
+ */
+static void
+check_read_watch (DBusTransport *transport)
+{
+ DBusTransportKdbus *kdbus_transport = (DBusTransportKdbus*) transport;
+ dbus_bool_t need_read_watch;
+
+ _dbus_verbose ("fd = %d\n",_kdbus_fd (kdbus_transport->kdbus));
+
+ if (transport->connection == NULL)
+ return;
+
+ if (transport->disconnected)
+ {
+ _dbus_assert (kdbus_transport->read_watch == NULL);
+ return;
+ }
+
+ _dbus_transport_ref (transport);
+
+ need_read_watch =
+ (_dbus_counter_get_size_value (transport->live_messages) < transport->max_live_messages_size) &&
+ (_dbus_counter_get_unix_fd_value (transport->live_messages) < transport->max_live_messages_unix_fds);
+
+ _dbus_verbose (" setting read watch enabled = %d\n", need_read_watch);
+
+ _dbus_connection_toggle_watch_unlocked (transport->connection,
+ kdbus_transport->read_watch,
+ need_read_watch);
+
+ _dbus_transport_unref (transport);
+}
+
+/**
+ * Copy-paste from socket transport.
+ */
+static void
+do_io_error (DBusTransport *transport)
+{
+ _dbus_transport_ref (transport);
+ _dbus_transport_disconnect (transport);
+ _dbus_transport_unref (transport);
+}
+
+/**
+ * Based on do_writing from socket transport.
+ * Removed authentication code and code related to encoded messages
+ * and adapted to kdbus transport.
+ * In socket transport returns false on out-of-memory. Here this won't happen,
+ * so it always returns TRUE.
+ */
+static dbus_bool_t
+do_writing (DBusTransport *transport)
+{
+ DBusTransportKdbus *kdbus_transport = (DBusTransportKdbus*) transport;
+ int total = 0;
+ dbus_bool_t oom = FALSE;
+
+ if (transport->disconnected)
+ {
+ _dbus_verbose ("Not connected, not writing anything\n");
+ return TRUE;
+ }
+
+ _dbus_verbose ("do_writing (), have_messages = %d, fd = %d\n",
+ _dbus_connection_has_messages_to_send_unlocked (transport->connection), _kdbus_fd (kdbus_transport->kdbus));
+
+ while (!transport->disconnected && _dbus_connection_has_messages_to_send_unlocked (transport->connection))
+ {
+ int bytes_written;
+ DBusMessage *message;
+ const DBusString *header;
+ const DBusString *body;
+ const char* pDestination;
+
+ if (total > kdbus_transport->max_bytes_written_per_iteration)
+ {
+ _dbus_verbose ("%d bytes exceeds %d bytes written per iteration, returning\n",
+ total, kdbus_transport->max_bytes_written_per_iteration);
+ goto out;
+ }
+
+ message = _dbus_connection_get_message_to_send (transport->connection);
+ _dbus_assert (message != NULL);
+ pDestination = dbus_message_get_destination (message);
+
+ if (pDestination)
+ {
+ int ret;
+
+ ret = capture_org_freedesktop_DBus ((DBusTransportKdbus*)transport, pDestination, message);
+ if (ret < 0) //error
+ {
+ bytes_written = -1;
+ goto written;
+ }
+ else if (ret == 0) //hello message captured and handled correctly
+ {
+ _dbus_message_get_network_data (message, &header, &body);
+ bytes_written = _dbus_string_get_length (header) + _dbus_string_get_length (body);
+ goto written;
+ }
+ //else send as regular message
+ }
+
+ bytes_written = kdbus_write_msg (kdbus_transport, message, pDestination);
+
+ written:
+ if (bytes_written < 0)
+ {
+ if (errno == ENOMEM)
+ {
+ oom = TRUE;
+ goto out;
+ }
+
+ /* EINTR already handled for us */
+
+ /* For some discussion of why we also ignore EPIPE here, see
+ * http://lists.freedesktop.org/archives/dbus/2008-March/009526.html
+ */
+
+ if (_dbus_get_is_errno_eagain_or_ewouldblock () || _dbus_get_is_errno_epipe ())
+ goto out;
+ else
+ {
+ _dbus_verbose ("Error writing to remote app: %s\n", _dbus_strerror_from_errno ());
+// do_io_error (transport);
+ /*TODO the comment above may cause side effects, but must be removed here
+ to not disconnect the connection. If side-effects appears, reporting errors for upper functions
+ must be rearranged.*/
+ goto out;
+ }
+ }
+ else
+ {
+#if defined (DBUS_ENABLE_VERBOSE_MODE) || !defined (DBUS_DISABLE_ASSERT)
+ int total_bytes_to_write;
+
+ _dbus_message_get_network_data (message, &header, &body);
+ total_bytes_to_write = _dbus_string_get_length (header)
+ + _dbus_string_get_length (body);
+ _dbus_verbose (" wrote %d bytes of %d\n", bytes_written,
+ total_bytes_to_write);
+
+ _dbus_assert (bytes_written == total_bytes_to_write);
+#endif
+ total += bytes_written;
+
+ _dbus_connection_message_sent_unlocked (transport->connection,
+ message);
+ }
+ }
+
+out:
+ if (oom)
+ return FALSE;
+ else
+ return TRUE;
+}
+
+/**
+ * Based on do_reading from socket transport.
+ * Removed authentication code and code related to encoded messages
+ * and adapted to kdbus transport.
+ * returns false on out-of-memory
+ */
+static dbus_bool_t
+do_reading (DBusTransport *transport)
+{
+ DBusTransportKdbus *kdbus_transport = (DBusTransportKdbus*) transport;
+ DBusString *buffer;
+ int bytes_read;
+ dbus_bool_t oom = FALSE;
+ int *fds, n_fds;
+ int total = 0;
+
+ _dbus_verbose ("fd = %d\n",_kdbus_fd (kdbus_transport->kdbus));
+
+ again:
+
+ /* See if we've exceeded max messages and need to disable reading */
+ if (kdbus_transport->activator == NULL)
+ check_read_watch (transport);
+
+ if (total > kdbus_transport->max_bytes_read_per_iteration)
+ {
+ _dbus_verbose ("%d bytes exceeds %d bytes read per iteration, returning\n",
+ total, kdbus_transport->max_bytes_read_per_iteration);
+ goto out;
+ }
+
+ _dbus_assert (kdbus_transport->read_watch != NULL ||
+ transport->disconnected);
+
+ if (transport->disconnected)
+ goto out;
+
+ if (!dbus_watch_get_enabled (kdbus_transport->read_watch))
+ return TRUE;
+
+ if (!_dbus_message_loader_get_unix_fds (transport->loader, &fds, &n_fds))
+ {
+ _dbus_verbose ("Out of memory reading file descriptors\n");
+ oom = TRUE;
+ goto out;
+ }
+ _dbus_message_loader_get_buffer (transport->loader, &buffer);
+
+ bytes_read = kdbus_read_message (kdbus_transport, buffer, fds, &n_fds);
+
+ if (bytes_read >= 0 && n_fds > 0)
+ _dbus_verbose ("Read %i unix fds\n", n_fds);
+
+ _dbus_message_loader_return_buffer (transport->loader,
+ buffer);
+ _dbus_message_loader_return_unix_fds (transport->loader, fds, bytes_read < 0 ? 0 : n_fds);
+
+ if (bytes_read < 0)
+ {
+ /* EINTR already handled for us */
+
+ if (_dbus_get_is_errno_enomem ())
+ {
+ _dbus_verbose ("Out of memory in read()/do_reading()\n");
+ oom = TRUE;
+ goto out;
+ }
+ else if (_dbus_get_is_errno_eagain_or_ewouldblock ())
+ goto out;
+ else
+ {
+ _dbus_verbose ("Error reading from remote app: %s\n",
+ _dbus_strerror_from_errno ());
+ do_io_error (transport);
+ goto out;
+ }
+ }
+ else if (bytes_read > 0)
+ {
+ _dbus_verbose (" read %d bytes\n", bytes_read);
+
+ total += bytes_read;
+
+ if (!_dbus_transport_queue_messages (transport))
+ {
+ oom = TRUE;
+ _dbus_verbose (" out of memory when queueing messages we just read in the transport\n");
+ goto out;
+ }
+
+ /* Try reading more data until we get EAGAIN and return, or
+ * exceed max bytes per iteration. If in blocking mode of
+ * course we'll block instead of returning.
+ */
+ goto again;
+ }
+ /* 0 == bytes_read is for kernel messages */
+
+ out:
+ if (oom)
+ return FALSE;
+ return TRUE;
+}
+
+/**
+ * Copy-paste from socket transport, with socket replaced by kdbus.
+ */
+static dbus_bool_t
+unix_error_with_read_to_come (DBusTransport *itransport,
+ DBusWatch *watch,
+ unsigned int flags)
+{
+ DBusTransportKdbus *transport = (DBusTransportKdbus *) itransport;
+
+ if (!((flags & DBUS_WATCH_HANGUP) || (flags & DBUS_WATCH_ERROR)))
+ return FALSE;
+
+ /* If we have a read watch enabled ...
+ we -might have data incoming ... => handle the HANGUP there */
+ if (watch != transport->read_watch && _dbus_watch_get_enabled (transport->read_watch))
+ return FALSE;
+
+ return TRUE;
+}
+
+/**
+ * Copy-paste from socket transport. Removed authentication related code
+ * and renamed socket_transport to kdbus_transport.
+ */
+static dbus_bool_t
+kdbus_handle_watch (DBusTransport *transport,
+ DBusWatch *watch,
+ unsigned int flags)
+{
+ DBusTransportKdbus *kdbus_transport = (DBusTransportKdbus*) transport;
+
+ _dbus_assert (watch == kdbus_transport->read_watch ||
+ watch == kdbus_transport->write_watch);
+ _dbus_assert (watch != NULL);
+
+ /* If we hit an error here on a write watch, don't disconnect the transport yet because data can
+ * still be in the buffer and do_reading may need several iteration to read
+ * it all (because of its max_bytes_read_per_iteration limit).
+ */
+ if (!(flags & DBUS_WATCH_READABLE) && unix_error_with_read_to_come (transport, watch, flags))
+ {
+ _dbus_verbose ("Hang up or error on watch\n");
+ _dbus_transport_disconnect (transport);
+ return TRUE;
+ }
+
+ if (watch == kdbus_transport->read_watch &&
+ (flags & DBUS_WATCH_READABLE))
+ {
+ _dbus_verbose ("handling read watch %p flags = %x\n",
+ watch, flags);
+
+ if (!do_reading (transport))
+ {
+ _dbus_verbose ("no memory to read\n");
+ return FALSE;
+ }
+ }
+ else if (watch == kdbus_transport->write_watch &&
+ (flags & DBUS_WATCH_WRITABLE))
+ {
+ _dbus_verbose ("handling write watch, have_outgoing_messages = %d\n",
+ _dbus_connection_has_messages_to_send_unlocked (transport->connection));
+
+ if (!do_writing (transport))
+ {
+ _dbus_verbose ("no memory to write\n");
+ return FALSE;
+ }
+
+ /* See if we still need the write watch */
+ check_write_watch (transport);
+ }
+
+ return TRUE;
+}
+
+/**
+ * Copy-paste from socket transport, but socket_transport renamed to kdbus_transport
+ * and _dbus_close_socket replaced with close ().
+ */
+static void
+kdbus_disconnect (DBusTransport *transport)
+{
+ DBusTransportKdbus *kdbus_transport = (DBusTransportKdbus*) transport;
+
+ _dbus_verbose ("\n");
+
+ free_watches (transport);
+
+ _kdbus_close (kdbus_transport->kdbus);
+}
+
+/**
+ * Copy-paste from socket transport. Renamed socket_transport to
+ * kdbus_transport and added setting authenticated to TRUE, because
+ * we do not perform authentication in kdbus, so we have mark is as already done
+ * to make everything work.
+ */
+static dbus_bool_t
+kdbus_connection_set (DBusTransport *transport)
+{
+ DBusTransportKdbus *kdbus_transport = (DBusTransportKdbus*) transport;
+
+ _dbus_watch_set_handler (kdbus_transport->write_watch,
+ _dbus_connection_handle_watch,
+ transport->connection, NULL);
+
+ _dbus_watch_set_handler (kdbus_transport->read_watch,
+ _dbus_connection_handle_watch,
+ transport->connection, NULL);
+
+ if (!_dbus_connection_add_watch_unlocked (transport->connection,
+ kdbus_transport->write_watch))
+ return FALSE;
+
+ if (!_dbus_connection_add_watch_unlocked (transport->connection,
+ kdbus_transport->read_watch))
+ {
+ _dbus_connection_remove_watch_unlocked (transport->connection,
+ kdbus_transport->write_watch);
+ return FALSE;
+ }
+
+ check_read_watch (transport);
+ check_write_watch (transport);
+
+ return TRUE;
+}
+
+/**
+ * Copy-paste from socket_transport.
+ * Socket_transport renamed to kdbus_transport
+ *
+ * Original dbus copy-pasted @todo comment below.
+ * @todo We need to have a way to wake up the select sleep if
+ * a new iteration request comes in with a flag (read/write) that
+ * we're not currently serving. Otherwise a call that just reads
+ * could block a write call forever (if there are no incoming
+ * messages).
+ */
+static void
+kdbus_do_iteration (DBusTransport *transport,
+ unsigned int flags,
+ int timeout_milliseconds)
+{
+ DBusTransportKdbus *kdbus_transport = (DBusTransportKdbus*) transport;
+ DBusPollFD poll_fd;
+ int poll_res;
+ int poll_timeout;
+
+ _dbus_verbose (" iteration flags = %s%s timeout = %d read_watch = %p write_watch = %p fd = %d\n",
+ flags & DBUS_ITERATION_DO_READING ? "read" : "",
+ flags & DBUS_ITERATION_DO_WRITING ? "write" : "",
+ timeout_milliseconds,
+ kdbus_transport->read_watch,
+ kdbus_transport->write_watch,
+ _kdbus_fd (kdbus_transport->kdbus));
+
+ poll_fd.fd = _kdbus_fd (kdbus_transport->kdbus);
+ poll_fd.events = 0;
+
+ /*
+ * TODO test this.
+ * This fix is for reply_with_error function.
+ * When timeout is set to -1 in client application,
+ * error messages are inserted directly to incoming queue and
+ * application hangs on dbus_poll.
+ */
+ if (_dbus_connection_get_n_incoming (transport->connection) > 0)
+ {
+ timeout_milliseconds = 0;
+ }
+ /* This is kind of a hack; if we have stuff to write, then try
+ * to avoid the poll. This is probably about a 5% speedup on an
+ * echo client/server.
+ *
+ * If both reading and writing were requested, we want to avoid this
+ * since it could have funky effects:
+ * - both ends spinning waiting for the other one to read
+ * data so they can finish writing
+ * - prioritizing all writing ahead of reading
+ */
+ if ((flags & DBUS_ITERATION_DO_WRITING) &&
+ !(flags & (DBUS_ITERATION_DO_READING | DBUS_ITERATION_BLOCK)) &&
+ !transport->disconnected &&
+ _dbus_connection_has_messages_to_send_unlocked (transport->connection))
+ {
+ do_writing (transport);
+
+ if (transport->disconnected ||
+ !_dbus_connection_has_messages_to_send_unlocked (transport->connection))
+ goto out;
+ }
+
+ /* If we get here, we decided to do the poll() after all */
+ _dbus_assert (kdbus_transport->read_watch);
+ if (flags & DBUS_ITERATION_DO_READING)
+ poll_fd.events |= _DBUS_POLLIN;
+
+ _dbus_assert (kdbus_transport->write_watch);
+ if (flags & DBUS_ITERATION_DO_WRITING)
+ poll_fd.events |= _DBUS_POLLOUT;
+
+ if (poll_fd.events)
+ {
+ if ( (flags & DBUS_ITERATION_BLOCK) && !(flags & DBUS_ITERATION_DO_WRITING))
+ poll_timeout = timeout_milliseconds;
+ else
+ poll_timeout = 0;
+
+ /* For blocking selects we drop the connection lock here
+ * to avoid blocking out connection access during a potentially
+ * indefinite blocking call. The io path is still protected
+ * by the io_path_cond condvar, so we won't reenter this.
+ */
+ if (flags & DBUS_ITERATION_BLOCK)
+ {
+ _dbus_verbose ("unlock pre poll\n");
+ _dbus_connection_unlock (transport->connection);
+ }
+
+ again:
+ poll_res = _dbus_poll (&poll_fd, 1, poll_timeout);
+
+ if (poll_res < 0 && _dbus_get_is_errno_eintr ())
+ goto again;
+
+ if (flags & DBUS_ITERATION_BLOCK)
+ {
+ _dbus_verbose ("lock post poll\n");
+ _dbus_connection_lock (transport->connection);
+ }
+
+ if (poll_res >= 0)
+ {
+ if (poll_res == 0)
+ poll_fd.revents = 0; /* some concern that posix does not guarantee this;
+ * valgrind flags it as an error. though it probably
+ * is guaranteed on linux at least.
+ */
+
+ if (poll_fd.revents & _DBUS_POLLERR)
+ do_io_error (transport);
+ else
+ {
+ dbus_bool_t need_read = (poll_fd.revents & _DBUS_POLLIN) > 0;
+
+ _dbus_verbose ("in iteration, need_read=%d\n",
+ need_read);
+
+ if (need_read && (flags & DBUS_ITERATION_DO_READING))
+ do_reading (transport);
+ /* We always be able to write to kdbus */
+ if (flags & DBUS_ITERATION_DO_WRITING)
+ do_writing (transport);
+ }
+ }
+ else
+ _dbus_verbose ("Error from _dbus_poll(): %s\n", _dbus_strerror_from_errno ());
+ }
+
+ out:
+ /* We need to install the write watch only if we did not
+ * successfully write everything. Note we need to be careful that we
+ * don't call check_write_watch *before* do_writing, since it's
+ * inefficient to add the write watch, and we can avoid it most of
+ * the time since we can write immediately.
+ *
+ * However, we MUST always call check_write_watch(); DBusConnection code
+ * relies on the fact that running an iteration will notice that
+ * messages are pending.
+ */
+ check_write_watch (transport);
+
+ _dbus_verbose (" ... leaving do_iteration()\n");
+}
+
+/**
+ * Copy-paste from socket transport.
+ */
+static void
+kdbus_live_messages_changed (DBusTransport *transport)
+{
+ /* See if we should look for incoming messages again */
+ check_read_watch (transport);
+}
+
+/**
+ * Gets file descriptor of the kdbus bus.
+ * @param transport transport
+ * @param fd_p place to write fd to
+ * @returns always TRUE
+ */
+static dbus_bool_t
+kdbus_get_kdbus_fd (DBusTransport *transport,
+ int *fd_p)
+{
+ DBusTransportKdbus *kdbus_transport = (DBusTransportKdbus*) transport;
+
+ *fd_p = _kdbus_fd (kdbus_transport->kdbus);
+
+ return TRUE;
+}
+
+static const DBusTransportVTable kdbus_vtable = {
+ transport_finalize,
+ kdbus_handle_watch,
+ kdbus_disconnect,
+ kdbus_connection_set,
+ kdbus_do_iteration,
+ kdbus_live_messages_changed,
+ kdbus_get_kdbus_fd
+};
+
+typedef unsigned long (*ConnectionInfoExtractField) (struct nameInfo *);
+
+static inline unsigned long
+_extract_name_info_userId (struct nameInfo *nameInfo)
+{
+ return nameInfo->userId;
+}
+
+static inline unsigned long
+_extract_name_info_processId (struct nameInfo *nameInfo)
+{
+ return nameInfo->processId;
+}
+
+static dbus_bool_t
+_dbus_transport_kdbus_get_connection_info_ulong_field (DBusTransport *transport,
+ ConnectionInfoExtractField function,
+ unsigned long *val)
+{
+ struct nameInfo conn_info;
+ int ret;
+
+ ret = _kdbus_connection_info_by_id (get_kdbus (transport),
+ _kdbus_id (get_kdbus (transport)),
+ FALSE,
+ &conn_info);
+ if (ret != 0)
+ return FALSE;
+
+ *val = function (&conn_info);
+ return TRUE;
+}
+
+static dbus_bool_t
+_dbus_transport_kdbus_get_unix_user (DBusTransport *transport,
+ unsigned long *uid)
+{
+ return _dbus_transport_kdbus_get_connection_info_ulong_field (transport,
+ _extract_name_info_userId,
+ uid);
+}
+
+static dbus_bool_t
+_dbus_transport_kdbus_get_unix_process_id (DBusTransport *transport,
+ unsigned long *pid)
+{
+ return _dbus_transport_kdbus_get_connection_info_ulong_field (transport,
+ _extract_name_info_processId,
+ pid);
+}
+
+/**
+ * Copy-paste from dbus_transport_socket with needed changes.
+ *
+ * Creates a new transport for the given kdbus file descriptor and address.
+ * The file descriptor must be nonblocking.
+ *
+ * @param fd the file descriptor.
+ * @param address the transport's address
+ * @returns the new transport, or #NULL if no memory.
+ */
+static DBusTransport*
+new_kdbus_transport (kdbus_t *kdbus,
+ const DBusString *address,
+ const char *activator)
+{
+ DBusTransportKdbus *kdbus_transport;
+
+ kdbus_transport = dbus_new0 (DBusTransportKdbus, 1);
+ if (kdbus_transport == NULL)
+ return NULL;
+
+ kdbus_transport->kdbus = kdbus;
+
+ kdbus_transport->write_watch = _dbus_watch_new (_kdbus_fd (kdbus),
+ DBUS_WATCH_WRITABLE,
+ FALSE,
+ NULL, NULL, NULL);
+ if (kdbus_transport->write_watch == NULL)
+ goto failed_2;
+
+ kdbus_transport->read_watch = _dbus_watch_new (_kdbus_fd (kdbus),
+ DBUS_WATCH_READABLE,
+ FALSE,
+ NULL, NULL, NULL);
+ if (kdbus_transport->read_watch == NULL)
+ goto failed_3;
+
+ if (!_dbus_transport_init_base_authenticated (&kdbus_transport->base,
+ &kdbus_vtable,
+ NULL, address))
+ goto failed_4;
+
+ _dbus_transport_set_get_unix_user_function (&kdbus_transport->base,
+ _dbus_transport_kdbus_get_unix_user);
+ _dbus_transport_set_get_unix_process_id_function (&kdbus_transport->base,
+ _dbus_transport_kdbus_get_unix_process_id);
+ _dbus_transport_set_assure_protocol_function (&kdbus_transport->base,
+ _dbus_message_assure_gvariant);
+
+ /* These values should probably be tunable or something. */
+ kdbus_transport->max_bytes_read_per_iteration = MAX_BYTES_PER_ITERATION;
+ kdbus_transport->max_bytes_written_per_iteration = MAX_BYTES_PER_ITERATION;
+
+ if (activator!=NULL)
+ {
+ int size = strlen (activator);
+ if (size)
+ {
+ kdbus_transport->activator = dbus_new (char, size + 1 );
+ if (kdbus_transport->activator != NULL)
+ strcpy (kdbus_transport->activator, activator);
+ else
+ goto failed_4;
+ }
+ }
+ else
+ kdbus_transport->activator = NULL;
+
+ kdbus_transport->matchmaker = matchmaker_new ();
+
+ kdbus_transport->client_serial = 1;
+
+ return (DBusTransport*) kdbus_transport;
+
+ failed_4:
+ _dbus_watch_invalidate (kdbus_transport->read_watch);
+ _dbus_watch_unref (kdbus_transport->read_watch);
+ failed_3:
+ _dbus_watch_invalidate (kdbus_transport->write_watch);
+ _dbus_watch_unref (kdbus_transport->write_watch);
+ failed_2:
+ dbus_free (kdbus_transport);
+ return NULL;
+}
+
+/**
+ * Connects to kdbus, creates and sets-up transport.
+ *
+ * @param path the path to the bus.
+ * @param error address where an error can be returned.
+ * @returns a new transport, or #NULL on failure.
+ */
+static DBusTransport*
+_dbus_transport_new_for_kdbus (const char *path,
+ const char *activator,
+ DBusError *error)
+{
+ int ret;
+ DBusTransport *transport;
+ DBusString address;
+ kdbus_t *kdbus;
+
+ const char *dbgenv = getenv ("G_DBUS_DEBUG");
+ if (dbgenv != NULL)
+ {
+ if (!strcmp (dbgenv, "message"))
+ debug = 1;
+ else if (!strcmp (dbgenv, "all"))
+ debug = 2;
+ }
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ if (!_dbus_string_init (&address))
+ {
+ dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+ return NULL;
+ }
+
+ if ((!_dbus_string_append (&address, DBUS_ADDRESS_KDBUS "path=")) || (!_dbus_string_append (&address, path)))
+ {
+ dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+ goto failed_0;
+ }
+
+ kdbus = _kdbus_new ();
+ if (NULL == kdbus)
+ {
+ dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+ goto failed_0;
+ }
+
+ ret = _kdbus_open (kdbus, path);
+ if (ret < 0)
+ {
+ dbus_set_error (error,
+ _dbus_error_from_errno (-ret),
+ "Failed to open file descriptor: %s: %s",
+ path,
+ _dbus_strerror (-ret));
+ goto failed_0_with_kdbus;
+ }
+
+ _dbus_verbose ("Successfully connected to kdbus bus %s\n", path);
+
+ transport = new_kdbus_transport (kdbus, &address, activator);
+ if (transport == NULL)
+ {
+ dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+ goto failed_1;
+ }
+
+ _dbus_string_free (&address);
+
+ return transport;
+
+failed_1:
+ _kdbus_close (kdbus);
+failed_0_with_kdbus:
+ _kdbus_free (kdbus);
+failed_0:
+ _dbus_string_free (&address);
+ return NULL;
+}
+
+
+/**
+ * Opens kdbus transport if method from address entry is kdbus
+ *
+ * @param entry the address entry to open
+ * @param transport_p return location for the opened transport
+ * @param error place to store error
+ * @returns result of the attempt as a DBusTransportOpenResult enum
+ */
+DBusTransportOpenResult
+_dbus_transport_open_kdbus (DBusAddressEntry *entry,
+ DBusTransport **transport_p,
+ DBusError *error)
+{
+ const char *method;
+
+ method = dbus_address_entry_get_method (entry);
+ _dbus_assert (method != NULL);
+
+ if (strcmp (method, "kernel") == 0)
+ {
+ const char *path = dbus_address_entry_get_value (entry, "path");
+ const char *activator = dbus_address_entry_get_value (entry, "activator");
+
+ if (path == NULL)
+ {
+ _dbus_set_bad_address (error, "kdbus", "path", NULL);
+ return DBUS_TRANSPORT_OPEN_BAD_ADDRESS;
+ }
+
+ *transport_p = _dbus_transport_new_for_kdbus (path, activator, error);
+
+ if (*transport_p == NULL)
+ {
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+ return DBUS_TRANSPORT_OPEN_DID_NOT_CONNECT;
+ }
+ else
+ {
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+ return DBUS_TRANSPORT_OPEN_OK;
+ }
+ }
+ else
+ {
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+ return DBUS_TRANSPORT_OPEN_NOT_HANDLED;
+ }
+}
+
+/** @} */
diff --git a/dbus/dbus-transport-kdbus.h b/dbus/dbus-transport-kdbus.h
new file mode 100644
index 00000000..75818f15
--- /dev/null
+++ b/dbus/dbus-transport-kdbus.h
@@ -0,0 +1,34 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-transport-kdbus.h kdbus subclasses of DBusTransport
+ *
+ * Copyright (C) 2013-2015 Samsung Electronics
+ *
+ * Licensed under the Academic Free License version 2.1
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+#ifndef DBUS_TRANSPORT_KDBUS_H_
+#define DBUS_TRANSPORT_KDBUS_H_
+
+#include "dbus-transport-protected.h"
+
+#define REGISTER_FLAG_MONITOR 1 << 0
+
+DBusTransportOpenResult _dbus_transport_open_kdbus (DBusAddressEntry *entry,
+ DBusTransport **transport_p,
+ DBusError *error);
+
+#endif
diff --git a/dbus/dbus-transport-protected.h b/dbus/dbus-transport-protected.h
index 396f0ffd..f6a5d501 100644
--- a/dbus/dbus-transport-protected.h
+++ b/dbus/dbus-transport-protected.h
@@ -71,6 +71,11 @@ struct DBusTransportVTable
/**< Get socket file descriptor */
};
+typedef dbus_bool_t (*DBusTransportGetUnixUserFunction) (DBusTransport *transport,
+ unsigned long *uid);
+typedef dbus_bool_t (*DBusTransportGetUnixPIDFunction) (DBusTransport *transport,
+ unsigned long *uid);
+typedef dbus_bool_t (*DBusTransportAssureProtocolFunction) (DBusMessage **message);
/**
* Object representing a transport such as a socket.
* A transport can shuttle messages from point A to point B,
@@ -109,6 +114,10 @@ struct DBusTransport
void *windows_user_data; /**< Data for windows_user_function */
DBusFreeFunction free_windows_user_data; /**< Function to free windows_user_data */
+
+ DBusTransportGetUnixUserFunction get_unix_user_function; /**< Function for getting Unix user ID */
+ DBusTransportGetUnixPIDFunction get_unix_process_id_function; /**< Function for getting Unix process ID */
+ DBusTransportAssureProtocolFunction assure_protocol_function; /**< Function for converting messages, if needed */
unsigned int disconnected : 1; /**< #TRUE if we are disconnected. */
unsigned int authenticated : 1; /**< Cache of auth state; use _dbus_transport_peek_is_authenticated() to query value */
@@ -123,8 +132,18 @@ dbus_bool_t _dbus_transport_init_base (DBusTransport *transport,
const DBusTransportVTable *vtable,
const DBusString *server_guid,
const DBusString *address);
+dbus_bool_t _dbus_transport_init_base_authenticated (DBusTransport *transport,
+ const DBusTransportVTable *vtable,
+ const DBusString *server_guid,
+ const DBusString *address);
void _dbus_transport_finalize_base (DBusTransport *transport);
+void _dbus_transport_set_get_unix_user_function (DBusTransport *transport,
+ DBusTransportGetUnixUserFunction function);
+void _dbus_transport_set_get_unix_process_id_function (DBusTransport *transport,
+ DBusTransportGetUnixPIDFunction function);
+void _dbus_transport_set_assure_protocol_function (DBusTransport *transport,
+ DBusTransportAssureProtocolFunction function);
typedef enum
{
diff --git a/dbus/dbus-transport.c b/dbus/dbus-transport.c
index ecc31827..eacc6b89 100644
--- a/dbus/dbus-transport.c
+++ b/dbus/dbus-transport.c
@@ -2,6 +2,7 @@
/* dbus-transport.c DBusTransport object (internal to D-Bus implementation)
*
* Copyright (C) 2002, 2003 Red Hat Inc.
+ * Copyright (C) 2013 Samsung Electronics
*
* Licensed under the Academic Free License version 2.1
*
@@ -32,6 +33,9 @@
#include "dbus-credentials.h"
#include "dbus-mainloop.h"
#include "dbus-message.h"
+#ifdef ENABLE_KDBUS_TRANSPORT
+#include "dbus-transport-kdbus.h"
+#endif
#ifdef DBUS_ENABLE_EMBEDDED_TESTS
#include "dbus-server-debug-pipe.h"
#endif
@@ -85,6 +89,58 @@ live_messages_notify (DBusCounter *counter,
_dbus_transport_unref (transport);
}
+static dbus_bool_t
+_dbus_transport_default_get_unix_user (DBusTransport *transport,
+ unsigned long *uid)
+{
+ DBusCredentials *auth_identity;
+
+ *uid = _DBUS_INT32_MAX; /* better than some root or system user in
+ * case of bugs in the caller. Caller should
+ * never use this value on purpose, however.
+ */
+
+ if (!transport->authenticated)
+ return FALSE;
+
+ auth_identity = _dbus_auth_get_identity (transport->auth);
+
+ if (_dbus_credentials_include (auth_identity,
+ DBUS_CREDENTIAL_UNIX_USER_ID))
+ {
+ *uid = _dbus_credentials_get_unix_uid (auth_identity);
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+static dbus_bool_t
+_dbus_transport_default_get_unix_process_id (DBusTransport *transport,
+ unsigned long *pid)
+{
+ DBusCredentials *auth_identity;
+
+ *pid = DBUS_PID_UNSET; /* Caller should never use this value on purpose,
+ * but we set it to a safe number, INT_MAX,
+ * just to root out possible bugs in bad callers.
+ */
+
+ if (!transport->authenticated)
+ return FALSE;
+
+ auth_identity = _dbus_auth_get_identity (transport->auth);
+
+ if (_dbus_credentials_include (auth_identity,
+ DBUS_CREDENTIAL_UNIX_PROCESS_ID))
+ {
+ *pid = _dbus_credentials_get_pid (auth_identity);
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
/**
* Initializes the base class members of DBusTransport. Chained up to
* by subclasses in their constructor. The server GUID is the
@@ -96,13 +152,15 @@ live_messages_notify (DBusCounter *counter,
* @param vtable the subclass vtable.
* @param server_guid non-#NULL if this transport is on the server side of a connection
* @param address the address of the transport
+ * @param with_auth TRUE if authentication should be used
* @returns #TRUE on success.
*/
-dbus_bool_t
-_dbus_transport_init_base (DBusTransport *transport,
- const DBusTransportVTable *vtable,
- const DBusString *server_guid,
- const DBusString *address)
+static dbus_bool_t
+_dbus_transport_init_base_with_auth (DBusTransport *transport,
+ const DBusTransportVTable *vtable,
+ const DBusString *server_guid,
+ const DBusString *address,
+ dbus_bool_t with_auth)
{
DBusMessageLoader *loader;
DBusAuth *auth;
@@ -117,7 +175,13 @@ _dbus_transport_init_base (DBusTransport *transport,
if (server_guid)
auth = _dbus_auth_server_new (server_guid);
else
- auth = _dbus_auth_client_new ();
+ {
+ if (with_auth)
+ auth = _dbus_auth_client_new ();
+ else
+ auth = _dbus_auth_client_new_authenticated ();
+ }
+
if (auth == NULL)
{
_dbus_message_loader_unref (loader);
@@ -203,9 +267,68 @@ _dbus_transport_init_base (DBusTransport *transport,
if (transport->address)
_dbus_verbose ("Initialized transport on address %s\n", transport->address);
+ transport->get_unix_user_function = _dbus_transport_default_get_unix_user;
+ transport->get_unix_process_id_function = _dbus_transport_default_get_unix_process_id;
+ transport->assure_protocol_function = _dbus_message_assure_dbus1;
+
return TRUE;
}
+dbus_bool_t
+_dbus_transport_assure_protocol_version (DBusTransport *transport,
+ DBusMessage **message)
+{
+ return transport->assure_protocol_function (message);
+}
+
+/**
+ * Initializes the base class members of DBusTransport. Chained up to
+ * by subclasses in their constructor. The server GUID is the
+ * globally unique ID for the server creating this connection
+ * and will be #NULL for the client side of a connection. The GUID
+ * is in hex format.
+ *
+ * @param transport the transport being created.
+ * @param vtable the subclass vtable.
+ * @param server_guid non-#NULL if this transport is on the server side of a connection
+ * @param address the address of the transport
+ * @returns #TRUE on success.
+ */
+dbus_bool_t
+_dbus_transport_init_base (DBusTransport *transport,
+ const DBusTransportVTable *vtable,
+ const DBusString *server_guid,
+ const DBusString *address)
+{
+ return _dbus_transport_init_base_with_auth (transport, vtable, server_guid, address, TRUE);
+}
+
+/**
+ * Initializes the base class members of DBusTransport. Chained up to
+ * by subclasses in their constructor. The server GUID is the
+ * globally unique ID for the server creating this connection
+ * and will be #NULL for the client side of a connection. The GUID
+ * is in hex format. Differs from _dbus_transport_init_base in that
+ * it sets auth as authenticated. This way auth negotiation is skipped.
+ *
+ * @param transport the transport being created.
+ * @param vtable the subclass vtable.
+ * @param server_guid non-#NULL if this transport is on the server side of a connection
+ * @param address the address of the transport
+ * @returns #TRUE on success.
+ */
+dbus_bool_t
+_dbus_transport_init_base_authenticated (DBusTransport *transport,
+ const DBusTransportVTable *vtable,
+ const DBusString *server_guid,
+ const DBusString *address)
+{
+ dbus_bool_t result = _dbus_transport_init_base_with_auth (transport, vtable, server_guid, address, FALSE);
+ if (result)
+ transport->authenticated = TRUE;
+ return result;
+}
+
/**
* Finalizes base class members of DBusTransport.
* Chained up to from subclass finalizers.
@@ -347,6 +470,9 @@ static const struct {
DBusTransport **transport_p,
DBusError *error);
} open_funcs[] = {
+#ifdef ENABLE_KDBUS_TRANSPORT
+ { _dbus_transport_open_kdbus },
+#endif
{ _dbus_transport_open_socket },
{ _dbus_transport_open_platform_specific },
{ _dbus_transport_open_autolaunch }
@@ -1300,6 +1426,47 @@ _dbus_transport_get_max_received_unix_fds (DBusTransport *transport)
}
/**
+ * Sets a function used to get UNIX user ID of the connection.
+ * See dbus_connection_get_unix_user().
+ *
+ * @param transport the transport
+ * @param function the getter function
+ */
+void
+_dbus_transport_set_get_unix_user_function (DBusTransport *transport,
+ DBusTransportGetUnixUserFunction function)
+{
+ transport->get_unix_user_function = function;
+}
+
+/**
+ * Sets a function used to get process ID of the connection.
+ * See dbus_connection_get_unix_process_id().
+ *
+ * @param transport the transport
+ * @param function the getter function
+ */
+void
+_dbus_transport_set_get_unix_process_id_function (DBusTransport *transport,
+ DBusTransportGetUnixPIDFunction function)
+{
+ transport->get_unix_process_id_function = function;
+}
+
+/**
+ * Sets a function used to assure that messages have correct protocol version
+ *
+ * @param transport the transport
+ * @param function the getter function
+ */
+void
+_dbus_transport_set_assure_protocol_function (DBusTransport *transport,
+ DBusTransportAssureProtocolFunction function)
+{
+ transport->assure_protocol_function = function;
+}
+
+/**
* See dbus_connection_get_unix_user().
*
* @param transport the transport
@@ -1310,26 +1477,9 @@ dbus_bool_t
_dbus_transport_get_unix_user (DBusTransport *transport,
unsigned long *uid)
{
- DBusCredentials *auth_identity;
-
- *uid = _DBUS_INT32_MAX; /* better than some root or system user in
- * case of bugs in the caller. Caller should
- * never use this value on purpose, however.
- */
-
- if (!transport->authenticated)
- return FALSE;
-
- auth_identity = _dbus_auth_get_identity (transport->auth);
-
- if (_dbus_credentials_include (auth_identity,
- DBUS_CREDENTIAL_UNIX_USER_ID))
- {
- *uid = _dbus_credentials_get_unix_uid (auth_identity);
- return TRUE;
- }
- else
+ if (transport->get_unix_user_function == NULL)
return FALSE;
+ return (transport->get_unix_user_function) (transport, uid);
}
/**
@@ -1343,26 +1493,9 @@ dbus_bool_t
_dbus_transport_get_unix_process_id (DBusTransport *transport,
unsigned long *pid)
{
- DBusCredentials *auth_identity;
-
- *pid = DBUS_PID_UNSET; /* Caller should never use this value on purpose,
- * but we set it to a safe number, INT_MAX,
- * just to root out possible bugs in bad callers.
- */
-
- if (!transport->authenticated)
- return FALSE;
-
- auth_identity = _dbus_auth_get_identity (transport->auth);
-
- if (_dbus_credentials_include (auth_identity,
- DBUS_CREDENTIAL_UNIX_PROCESS_ID))
- {
- *pid = _dbus_credentials_get_pid (auth_identity);
- return TRUE;
- }
- else
+ if (transport->get_unix_process_id_function == NULL)
return FALSE;
+ return (transport->get_unix_process_id_function) (transport, pid);
}
/**
diff --git a/dbus/dbus-transport.h b/dbus/dbus-transport.h
index 80fa24ef..0c9458d1 100644
--- a/dbus/dbus-transport.h
+++ b/dbus/dbus-transport.h
@@ -98,6 +98,10 @@ dbus_bool_t _dbus_transport_set_auth_mechanisms (DBusTransport
void _dbus_transport_set_allow_anonymous (DBusTransport *transport,
dbus_bool_t value);
+dbus_bool_t _dbus_transport_assure_protocol_version (DBusTransport *transport,
+ DBusMessage **message);
+
+
/* if DBUS_ENABLE_STATS */
void _dbus_transport_get_stats (DBusTransport *transport,
dbus_uint32_t *queue_bytes,
diff --git a/dbus/kdbus-common.c b/dbus/kdbus-common.c
new file mode 100644
index 00000000..facd45a2
--- /dev/null
+++ b/dbus/kdbus-common.c
@@ -0,0 +1,1026 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* kdbus-common.c kdbus related utils for daemon and libdbus
+ *
+ * Copyright (C) 2013 Samsung Electronics
+ *
+ * Licensed under the Academic Free License version 2.1
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version and under the terms of the GNU
+ * Lesser General Public License as published by the
+ * Free Software Foundation; either version 2.1 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+#include "kdbus.h"
+#include "kdbus-common.h"
+#include "dbus-transport-kdbus.h"
+#include <string.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include <dbus/dbus-internals.h>
+#include <dbus/dbus-shared.h>
+#include "dbus-signals.h"
+#include <stdio.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+
+struct kdbus_t
+{
+ int fd; /**< File descriptor */
+ void *mmap_ptr; /**< Mapped memory where kdbus (kernel) writes
+ * messages incoming to us.
+ */
+ size_t pool_size; /**< Size of mapped memory */
+ __u64 id; /**< unique id of the connection */
+ char bus_id[sizeof(((struct kdbus_cmd_hello *)(0))->id128)]; /**< id of the bus */
+ struct kdbus_bloom_parameter bloom; /**< bloom parameters*/
+};
+
+/** temporary accessors - to delete soon */
+int _kdbus_fd (kdbus_t *kdbus) { return kdbus->fd; }
+void *_kdbus_mmap_ptr (kdbus_t *kdbus) { return kdbus->mmap_ptr; }
+dbus_uint64_t _kdbus_id (kdbus_t *kdbus) { return kdbus->id; }
+char *_kdbus_bus_id (kdbus_t *kdbus) { return kdbus->bus_id; }
+dbus_uint64_t _kdbus_bus_id_size (void) { return sizeof(((struct kdbus_t *)(0))->bus_id); }
+struct kdbus_bloom_parameter *_kdbus_bloom (kdbus_t *kdbus) { return &kdbus->bloom; }
+
+
+
+/* ALIGN8 and KDBUS_FOREACH taken from systemd */
+#define ALIGN8(l) (((l) + 7) & ~7)
+#define KDBUS_FOREACH(iter, first, _size) \
+ for (iter = (first); \
+ ((uint8_t *)(iter) < (uint8_t *)(first) + (_size)) && \
+ ((uint8_t *)(iter) >= (uint8_t *)(first)); \
+ iter = (void*)(((uint8_t *)iter) + ALIGN8((iter)->size)))
+
+static int
+safe_ioctl (int fd,
+ unsigned long request,
+ void *data)
+{
+ int ret;
+
+ do {
+ ret = ioctl (fd, request, data);
+ }
+ while (-1 == ret && EINTR == errno);
+
+ return ret;
+}
+
+static int
+free_by_offset (kdbus_t *kdbus,
+ __u64 offset)
+{
+ struct kdbus_cmd_free cmd;
+
+ cmd.size = sizeof (cmd);
+ cmd.offset = offset;
+ cmd.flags = 0;
+
+ if (safe_ioctl (kdbus->fd, KDBUS_CMD_FREE, &cmd )!= 0)
+ return errno;
+
+ return 0;
+}
+
+static void make_item_name(const char *name, struct kdbus_item *item)
+{
+ size_t len = strlen(name) + 1;
+ item->size = KDBUS_ITEM_HEADER_SIZE + len;
+ item->type = KDBUS_ITEM_NAME;
+
+ memcpy(item->str, name, len);
+}
+
+/**
+ * Adds an item in the current position of items array.
+ *
+ * @param item item to fill
+ * @param item_type type of the item
+ * @param string value of the item
+ * @param string_size size of the value
+ * @returns pointer to the next item
+ */
+struct kdbus_item *
+_kdbus_item_add_string (struct kdbus_item *item,
+ dbus_uint64_t item_type,
+ const char *item_string,
+ dbus_uint64_t item_string_size)
+{
+ item->size = KDBUS_ITEM_HEADER_SIZE + item_string_size;
+ item->type = item_type;
+ memcpy (item->str, item_string, item_string_size);
+ return KDBUS_ITEM_NEXT (item);
+}
+
+struct kdbus_item *
+_kdbus_item_add_payload_memfd (struct kdbus_item *item,
+ dbus_uint64_t start,
+ dbus_uint64_t size,
+ int fd)
+{
+ item->type = KDBUS_ITEM_PAYLOAD_MEMFD;
+ item->size = KDBUS_ITEM_HEADER_SIZE + sizeof (struct kdbus_memfd);
+ item->memfd.start = start;
+ item->memfd.size = size;
+ item->memfd.fd = fd;
+ return KDBUS_ITEM_NEXT (item);
+}
+
+struct kdbus_item *
+_kdbus_item_add_payload_vec (struct kdbus_item *item,
+ dbus_uint64_t size,
+ dbus_uint64_t address_or_offset)
+{
+ item->type = KDBUS_ITEM_PAYLOAD_VEC;
+ item->size = KDBUS_ITEM_HEADER_SIZE + sizeof (struct kdbus_vec);
+ item->vec.size = size;
+ item->vec.address = address_or_offset;
+ return KDBUS_ITEM_NEXT (item);
+}
+
+struct kdbus_item *
+_kdbus_item_add_fds (struct kdbus_item *item,
+ const int *fds,
+ int fds_count)
+{
+ item->type = KDBUS_ITEM_FDS;
+ item->size = KDBUS_ITEM_HEADER_SIZE + fds_count * sizeof (int);
+ memcpy (item->fds, fds, fds_count * sizeof (int));
+ return KDBUS_ITEM_NEXT (item);
+}
+
+struct kdbus_item *
+_kdbus_item_add_bloom_filter (struct kdbus_item *item,
+ dbus_uint64_t data_size,
+ struct kdbus_bloom_filter **out_ptr)
+{
+ item->type = KDBUS_ITEM_BLOOM_FILTER;
+ item->size = KDBUS_ITEM_HEADER_SIZE + sizeof (struct kdbus_bloom_filter) + data_size;
+ *out_ptr = &item->bloom_filter;
+ return KDBUS_ITEM_NEXT (item);
+}
+
+struct kdbus_item *
+_kdbus_item_add_name_change (struct kdbus_item *item,
+ dbus_uint64_t old_id,
+ dbus_uint64_t old_id_flags,
+ dbus_uint64_t new_id,
+ dbus_uint64_t new_id_flags)
+{
+ item->size = KDBUS_ITEM_HEADER_SIZE + sizeof (struct kdbus_notify_name_change);
+ item->type = KDBUS_ITEM_NAME_CHANGE;
+ item->name_change.old_id.id = old_id;
+ item->name_change.old_id.flags = old_id_flags;
+ item->name_change.new_id.id = new_id;
+ item->name_change.new_id.flags = new_id_flags;
+ return KDBUS_ITEM_NEXT (item);
+}
+
+struct kdbus_item *
+_kdbus_item_add_id_add (struct kdbus_item *item,
+ dbus_uint64_t id,
+ dbus_uint64_t id_flags)
+{
+ item->size = KDBUS_ITEM_HEADER_SIZE + sizeof (struct kdbus_notify_id_change);
+ item->type = KDBUS_ITEM_ID_ADD;
+ item->id_change.id = id;
+ item->id_change.flags = id_flags;
+ return KDBUS_ITEM_NEXT (item);
+}
+
+struct kdbus_item *
+_kdbus_item_add_id (struct kdbus_item *item,
+ dbus_uint64_t id)
+{
+ item->size = KDBUS_ITEM_HEADER_SIZE + sizeof (struct kdbus_notify_id_change);
+ item->type = KDBUS_ITEM_ID;
+ item->id = id;
+ return KDBUS_ITEM_NEXT (item);
+}
+
+struct kdbus_item *
+_kdbus_item_add_bloom_mask (struct kdbus_item *item,
+ dbus_uint64_t *bloom,
+ dbus_uint64_t bloom_size)
+{
+ item->size = KDBUS_ITEM_HEADER_SIZE + bloom_size;
+ item->type = KDBUS_ITEM_BLOOM_MASK;
+ memcpy (item->data, bloom, bloom_size);
+ return KDBUS_ITEM_NEXT (item);
+}
+
+static inline void *
+get_from_offset (kdbus_t *kdbus,
+ __u64 offset)
+{
+ return ((char *)kdbus->mmap_ptr) + offset;
+}
+
+kdbus_t *
+_kdbus_new ()
+{
+ return dbus_new (kdbus_t, 1);
+}
+
+void
+_kdbus_free (kdbus_t *kdbus)
+{
+ dbus_free (kdbus);
+}
+
+/**
+ * Opens a connection to the kdbus bus
+ *
+ * @param kdbus kdbus object
+ * @param path the path to kdbus bus
+ * @returns 0 on success, -errno on failure
+ */
+int
+_kdbus_open (kdbus_t *kdbus, const char *path)
+{
+ int fd = open (path, O_RDWR|O_CLOEXEC|O_NONBLOCK);
+ if (-1 == fd)
+ return -errno;
+
+ kdbus->fd = fd;
+ return 0;
+}
+
+int
+_kdbus_close (kdbus_t *kdbus)
+{
+ int ret;
+ int errclose = 0;
+ int errunmap = 0;
+
+ do
+ {
+ ret = close (kdbus->fd);
+ } while (-1 == ret && EINTR == errno);
+ if (-1 == ret)
+ errclose = errno;
+
+ ret = munmap (kdbus->mmap_ptr, kdbus->pool_size);
+ if (-1 == ret)
+ errunmap = errno;
+
+ if (0 != errclose)
+ return -errclose;
+ if (0 != errunmap)
+ return -errunmap;
+ return 0;
+}
+
+int
+_kdbus_hello (kdbus_t *kdbus,
+ dbus_uint64_t flags,
+ dbus_uint64_t attach_flags_send,
+ dbus_uint64_t attach_flags_recv,
+ dbus_uint64_t pool_size,
+ const char *activator_name,
+ const char *connection_name)
+{
+ struct kdbus_cmd_hello *hello;
+ struct kdbus_item *item, *items;
+ __u64 hello_size;
+ size_t activator_name_size = 0;
+ size_t connection_name_size = 0;
+ __u64 offset;
+ __u64 items_size;
+
+ hello_size = sizeof (struct kdbus_cmd_hello);
+
+ if (NULL != activator_name)
+ {
+ activator_name_size = strlen (activator_name) + 1;
+ hello_size += KDBUS_ITEM_SIZE (activator_name_size);
+ }
+
+ if (NULL != connection_name)
+ {
+ connection_name_size = strlen (connection_name) + 1;
+ hello_size += KDBUS_ITEM_SIZE (connection_name_size);
+ }
+
+ hello = dbus_malloc (hello_size);
+ if (NULL == hello)
+ return -ENOMEM;
+
+ hello->flags = flags;
+ hello->attach_flags_send = attach_flags_send;
+ hello->attach_flags_recv = attach_flags_recv;
+ hello->pool_size = pool_size;
+
+ item = hello->items;
+ if (connection_name_size > 0)
+ item = _kdbus_item_add_string (item,
+ KDBUS_ITEM_CONN_DESCRIPTION,
+ connection_name,
+ connection_name_size);
+ if (activator_name_size > 0)
+ {
+ _kdbus_item_add_string (item,
+ KDBUS_ITEM_NAME,
+ activator_name,
+ activator_name_size);
+ hello->flags |= KDBUS_HELLO_ACTIVATOR;
+ }
+
+ hello->size = hello_size;
+
+ if (safe_ioctl (kdbus->fd, KDBUS_CMD_HELLO, hello) != 0)
+ {
+ dbus_free (hello);
+ return -errno;
+ }
+
+ kdbus->id = hello->id;
+ memcpy (kdbus->bus_id, hello->id128, sizeof (kdbus->bus_id));
+
+ offset = hello->offset;
+ items_size = hello->items_size;
+ dbus_free (hello);
+
+ kdbus->mmap_ptr = mmap (NULL, pool_size, PROT_READ, MAP_SHARED, kdbus->fd, 0);
+ if (MAP_FAILED == kdbus->mmap_ptr)
+ return -errno;
+
+ kdbus->pool_size = pool_size;
+
+ items = get_from_offset (kdbus, offset);
+ KDBUS_FOREACH (item, items, items_size)
+ {
+ if (KDBUS_ITEM_BLOOM_PARAMETER == item->type)
+ kdbus->bloom = item->bloom_parameter;
+ }
+
+ return 0;
+}
+
+int
+_kdbus_send (kdbus_t *kdbus,
+ dbus_uint64_t flags,
+ struct kdbus_msg *msg,
+ struct kdbus_msg **msg_reply)
+{
+ struct kdbus_cmd_send cmd;
+
+ cmd.size = sizeof(cmd);
+ cmd.msg_address = (__u64)msg;
+ cmd.flags = flags;
+
+ if (-1 == safe_ioctl (kdbus->fd, KDBUS_CMD_SEND, &cmd))
+ return errno;
+
+ if (flags & KDBUS_SEND_SYNC_REPLY)
+ {
+ if (NULL != msg_reply)
+ *msg_reply = get_from_offset (kdbus, cmd.reply.offset);
+ else
+ free_by_offset (kdbus, cmd.reply.offset);
+ }
+
+ return 0;
+}
+
+int
+_kdbus_recv (kdbus_t *kdbus,
+ dbus_uint64_t flags,
+ dbus_int64_t priority,
+ struct kdbus_msg **msg)
+{
+ struct kdbus_cmd_recv cmd;
+
+ cmd.size = sizeof (cmd);
+ cmd.flags = flags;
+ cmd.priority = priority;
+
+ if (-1 == safe_ioctl (kdbus->fd, KDBUS_CMD_RECV, &cmd))
+ return errno;
+
+ *msg = get_from_offset (kdbus, cmd.msg.offset);
+
+ return 0;
+}
+
+int
+_kdbus_list (kdbus_t *kdbus,
+ dbus_uint64_t flags,
+ struct kdbus_info **name_list,
+ dbus_uint64_t *list_size)
+{
+ struct kdbus_cmd_list cmd;
+
+ cmd.size = sizeof (cmd);
+ cmd.flags = flags;
+
+ if (-1 == safe_ioctl (kdbus->fd, KDBUS_CMD_LIST, &cmd))
+ return errno;
+
+ *name_list = get_from_offset (kdbus, cmd.offset);
+ *list_size = cmd.list_size;
+
+ return 0;
+}
+
+struct kdbus_cmd_match *
+_kdbus_new_cmd_match (kdbus_t *kdbus,
+ dbus_uint64_t items_size,
+ dbus_uint64_t flags,
+ dbus_uint64_t cookie)
+{
+ struct kdbus_cmd_match *cmd;
+ dbus_uint64_t cmd_size = sizeof (*cmd) + items_size;
+ cmd = dbus_malloc (cmd_size);
+ if (NULL == cmd)
+ return NULL;
+
+ cmd->size = cmd_size;
+ cmd->flags = flags;
+ cmd->cookie = cookie;
+
+ return cmd;
+}
+
+void
+_kdbus_free_cmd_match (struct kdbus_cmd_match *cmd)
+{
+ dbus_free (cmd);
+}
+
+int
+_kdbus_add_match_name_change (kdbus_t *kdbus,
+ dbus_uint64_t flags,
+ dbus_uint64_t cookie,
+ dbus_uint64_t old_id,
+ dbus_uint64_t old_id_flags,
+ dbus_uint64_t new_id,
+ dbus_uint64_t new_id_flags)
+{
+ struct kdbus_cmd_match *cmd;
+ struct kdbus_item *item;
+ int ret;
+
+ cmd = _kdbus_new_cmd_match (kdbus,
+ KDBUS_ITEM_SIZE (sizeof (struct kdbus_notify_name_change)),
+ flags,
+ cookie);
+ if (NULL == cmd)
+ return ENOMEM;
+
+ item = cmd->items;
+ _kdbus_item_add_name_change (item,
+ old_id, old_id_flags,
+ new_id, new_id_flags);
+
+ ret = safe_ioctl (kdbus->fd, KDBUS_CMD_MATCH_ADD, cmd);
+ if (0 == ret)
+ {
+ item->type = KDBUS_ITEM_NAME_ADD;
+ ret = safe_ioctl (kdbus->fd, KDBUS_CMD_MATCH_ADD, cmd);
+ if (0 == ret)
+ {
+ item->type = KDBUS_ITEM_NAME_REMOVE;
+ ret = safe_ioctl (kdbus->fd, KDBUS_CMD_MATCH_ADD, cmd);
+ }
+ }
+
+ if (0 != ret)
+ ret = errno;
+
+ _kdbus_free_cmd_match (cmd);
+ return ret;
+}
+
+int
+_kdbus_add_match_id_change (kdbus_t *kdbus,
+ dbus_uint64_t flags,
+ dbus_uint64_t cookie,
+ dbus_uint64_t id,
+ dbus_uint64_t id_flags)
+{
+ struct kdbus_cmd_match *cmd;
+ struct kdbus_item *item;
+ int ret;
+
+ cmd = _kdbus_new_cmd_match (kdbus,
+ KDBUS_ITEM_SIZE (sizeof (struct kdbus_notify_id_change)),
+ flags,
+ cookie);
+ if (NULL == cmd)
+ return ENOMEM;
+
+ item = cmd->items;
+ _kdbus_item_add_id_add (item, id, id_flags);
+
+ ret = safe_ioctl (kdbus->fd, KDBUS_CMD_MATCH_ADD, cmd);
+ if (0 == ret)
+ {
+ item->type = KDBUS_ITEM_ID_REMOVE;
+ ret = safe_ioctl (kdbus->fd, KDBUS_CMD_MATCH_ADD, cmd);
+ }
+
+ if (0 != ret)
+ ret = errno;
+
+ _kdbus_free_cmd_match (cmd);
+ return ret;
+}
+
+int _kdbus_add_match (kdbus_t *kdbus,
+ struct kdbus_cmd_match *cmd)
+{
+ int ret = safe_ioctl (kdbus->fd, KDBUS_CMD_MATCH_ADD, cmd);
+ if (0 != ret)
+ return errno;
+
+ return 0;
+}
+
+/**
+ * Allocates and initializes kdbus message structure.
+ * @param kdbus kdbus object
+ * @param size_for_items size of items that will be attached to this message
+ * @param flags flags for message
+ * @returns initialized kdbus message or NULL if malloc failed
+ */
+struct kdbus_msg *
+_kdbus_new_msg (kdbus_t *kdbus,
+ dbus_uint64_t size_for_items,
+ dbus_uint64_t flags,
+ dbus_int64_t priority,
+ dbus_uint64_t dst_id,
+ dbus_uint64_t src_id,
+ enum kdbus_payload_type payload_type,
+ dbus_uint64_t cookie,
+ dbus_uint64_t timeout_ns_or_cookie_reply)
+{
+ struct kdbus_msg *msg;
+ dbus_uint64_t msg_size = sizeof (struct kdbus_msg) + size_for_items;
+
+ msg = dbus_malloc (msg_size);
+ if (NULL == msg)
+ return NULL;
+
+ msg->size = msg_size;
+ msg->flags = flags;
+ msg->priority = priority;
+ msg->dst_id = dst_id;
+ msg->src_id = src_id;
+ msg->payload_type = payload_type;
+ msg->cookie = cookie;
+ msg->timeout_ns = timeout_ns_or_cookie_reply;
+
+ return msg;
+}
+
+void
+_kdbus_free_msg (struct kdbus_msg *msg)
+{
+ dbus_free (msg);
+}
+
+int
+_kdbus_free_mem (kdbus_t *kdbus, void *mem)
+{
+ char *base_ptr = kdbus->mmap_ptr;
+ char *mem_ptr = (char *)mem;
+
+ return free_by_offset (kdbus, mem_ptr - base_ptr);
+}
+
+/**
+ * Computes size of items that will be attached to a message.
+ *
+ * @param kdbus kdbus object
+ * @param destination Well-known name or NULL. If NULL, dst_id must be supplied.
+ * @param dst_id Numeric id of recipient. Ignored if name is not NULL.
+ * @param body_size Size of message body (may be 0).
+ * @param use_memfd Flag to build memfd message.
+ * @param fds_count Number of file descriptors sent in the message.
+ * @returns size in bytes needed for the message object
+ */
+dbus_uint64_t
+_kdbus_compute_msg_items_size (kdbus_t *kdbus,
+ const char *destination,
+ dbus_uint64_t dst_id,
+ dbus_uint64_t body_size,
+ dbus_bool_t use_memfd,
+ int fds_count)
+{
+ dbus_uint64_t items_size = 0;
+
+ if (use_memfd)
+ {
+ items_size += KDBUS_ITEM_SIZE (sizeof (struct kdbus_memfd));
+ }
+ else
+ {
+ dbus_uint64_t vectors = (body_size + KDBUS_MSG_MAX_PAYLOAD_VEC_SIZE - 1)
+ / KDBUS_MSG_MAX_PAYLOAD_VEC_SIZE;
+ /* 1st vector -> for header */
+ items_size += KDBUS_ITEM_SIZE (sizeof (struct kdbus_vec));
+ /* subsequent vectors -> parts of body */
+ items_size += vectors * KDBUS_ITEM_SIZE (sizeof (struct kdbus_vec));
+ }
+
+ if (fds_count > 0)
+ items_size += KDBUS_ITEM_SIZE (sizeof (int) * fds_count);
+
+ if (destination)
+ items_size += KDBUS_ITEM_SIZE (strlen (destination) + 1);
+ else if (KDBUS_DST_ID_BROADCAST == dst_id)
+ items_size += KDBUS_ITEM_SIZE (sizeof (struct kdbus_bloom_filter))
+ + kdbus->bloom.size;
+ return items_size;
+}
+
+/**
+ *
+ * Asks the bus to assign the given name to the connection.
+ *
+ * Use same flags as original dbus version with one exception below.
+ * Result flag #DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER is currently
+ * never returned by kdbus, instead DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER
+ * is returned by kdbus.
+ *
+ * @param transport transport of the connection
+ * @param name the name to request
+ * @param flags flags
+ * @returns a DBus result code on success, -errno on error
+ */
+int
+request_kdbus_name(DBusTransport *transport,
+ const char *name,
+ const __u64 flags)
+{
+ struct kdbus_cmd *cmd_name;
+ int fd;
+ size_t len = strlen(name) + 1;
+
+ __u64 size = sizeof(*cmd_name) + KDBUS_ITEM_SIZE(len);
+ __u64 flags_kdbus = 0;
+
+ if(!_dbus_transport_get_socket_fd (transport, &fd))
+ return FALSE;
+
+ cmd_name = alloca(size);
+ cmd_name->size = size;
+
+ if(flags & DBUS_NAME_FLAG_ALLOW_REPLACEMENT)
+ flags_kdbus |= KDBUS_NAME_ALLOW_REPLACEMENT;
+ if(!(flags & DBUS_NAME_FLAG_DO_NOT_QUEUE))
+ flags_kdbus |= KDBUS_NAME_QUEUE;
+ if(flags & DBUS_NAME_FLAG_REPLACE_EXISTING)
+ flags_kdbus |= KDBUS_NAME_REPLACE_EXISTING;
+
+ cmd_name->flags = flags_kdbus;
+ make_item_name(name, &(cmd_name->items[0]));
+
+ _dbus_verbose("Request name - flags sent: 0x%llx !!!!!!!!!\n", cmd_name->flags);
+
+ if (ioctl(fd, KDBUS_CMD_NAME_ACQUIRE, cmd_name) < 0)
+ {
+ _dbus_verbose ("error acquiring name '%s': %m, %d\n", name, errno);
+ if(errno == EEXIST)
+ return DBUS_REQUEST_NAME_REPLY_EXISTS;
+ if(errno == EALREADY)
+ return DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER;
+ return -errno;
+ }
+ else if ((cmd_name->return_flags & KDBUS_NAME_PRIMARY)
+ && !(cmd_name->return_flags & KDBUS_NAME_ACQUIRED))
+ return DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER;
+
+ _dbus_verbose("Request name - received flag: 0x%llx !!!!!!!!!\n", cmd_name->flags);
+
+ if(cmd_name->return_flags & KDBUS_NAME_IN_QUEUE)
+ return DBUS_REQUEST_NAME_REPLY_IN_QUEUE;
+
+ return DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER;
+}
+
+/**
+ *
+ * Releases well-known name - the connections resign from the name
+ * which can be then assigned to another connection or the connection
+ * is being removed from the queue for that name
+ *
+ * @param fd - file descriptor of the connection
+ * @param name the name to request
+ * @param id unique id of the connection for which the name is being released
+ * @returns a DBus result code on success, -errno on error
+ */
+int
+release_kdbus_name(DBusTransport *transport,
+ const char *name)
+{
+ struct kdbus_cmd *cmd_name;
+ int fd;
+
+ size_t len = strlen(name)+1;
+ __u64 size = sizeof(*cmd_name) + KDBUS_ITEM_SIZE(len);
+
+ if(!_dbus_transport_get_socket_fd (transport, &fd))
+ return FALSE;
+
+ cmd_name = alloca(size);
+ cmd_name->size = size;
+ cmd_name->flags = 0;
+ make_item_name(name, &(cmd_name->items[0]));
+
+ if (ioctl(fd, KDBUS_CMD_NAME_RELEASE, cmd_name))
+ {
+ if((errno == ESRCH))
+ return DBUS_RELEASE_NAME_REPLY_NON_EXISTENT;
+ else if (errno == EADDRINUSE)
+ return DBUS_RELEASE_NAME_REPLY_NOT_OWNER;
+ _dbus_verbose ("error releasing name '%s'. Error: %m, %d\n", name, errno);
+ return -errno;
+ }
+
+ _dbus_verbose("Name '%s' released\n", name);
+
+ return DBUS_RELEASE_NAME_REPLY_RELEASED;
+}
+
+static int
+decode_connection_info (struct kdbus_info *connection_info,
+ struct nameInfo *pInfo,
+ dbus_bool_t get_sec_label)
+{
+ struct kdbus_item *item;
+
+ memset (pInfo, 0, sizeof(*pInfo));
+
+ pInfo->uniqueId = connection_info->id;
+ pInfo->flags = connection_info->flags;
+
+ item = connection_info->items;
+
+ while ((uint8_t *)item < ((uint8_t *)connection_info) + connection_info->size)
+ {
+ switch (item->type)
+ {
+ case KDBUS_ITEM_PIDS:
+ pInfo->processId = item->pids.pid;
+ break;
+ case KDBUS_ITEM_CREDS:
+ pInfo->userId = item->creds.uid;
+ break;
+ case KDBUS_ITEM_SECLABEL:
+ if (get_sec_label)
+ {
+ pInfo->sec_label_len = item->size - KDBUS_ITEM_HEADER_SIZE - 1;
+ if (0 != pInfo->sec_label_len)
+ {
+ pInfo->sec_label = dbus_malloc (pInfo->sec_label_len);
+ if (NULL == pInfo->sec_label)
+ return ENOMEM;
+
+ memcpy (pInfo->sec_label, item->data, pInfo->sec_label_len);
+ }
+ }
+ break;
+ }
+
+ item = KDBUS_ITEM_NEXT (item);
+ }
+ return 0;
+}
+
+static int
+process_connection_info_cmd (kdbus_t *kdbus,
+ struct kdbus_cmd_info *cmd,
+ struct nameInfo *pInfo,
+ dbus_bool_t get_sec_label)
+{
+ int ret;
+ struct kdbus_info *kdbus_info;
+
+ if (NULL == cmd)
+ return -1;
+
+ ret = safe_ioctl (kdbus->fd, KDBUS_CMD_CONN_INFO, cmd);
+
+ if (ret < 0)
+ {
+ pInfo->uniqueId = 0;
+ return errno;
+ }
+
+ kdbus_info = get_from_offset (kdbus, cmd->offset);
+ ret = decode_connection_info (kdbus_info,
+ pInfo,
+ get_sec_label);
+ if (ret != 0)
+ return ret;
+
+ ret = free_by_offset (kdbus, cmd->offset);
+ if (ret != 0)
+ {
+ _dbus_verbose("kdbus error freeing pool: %d (%m)\n", errno);
+ if (get_sec_label)
+ {
+ free(pInfo->sec_label);
+ pInfo->sec_label = NULL;
+ }
+ }
+
+ dbus_free (cmd);
+
+ return ret;
+}
+
+static struct kdbus_cmd_info *
+prepare_connection_info_cmd (dbus_uint64_t id,
+ const char *name,
+ dbus_bool_t get_sec_label)
+{
+ struct kdbus_cmd_info *cmd;
+ dbus_uint64_t size = sizeof(*cmd);
+ if (NULL != name)
+ {
+ size += KDBUS_ITEM_SIZE (strlen (name) + 1);
+ }
+ cmd = dbus_malloc (size);
+ if (NULL == cmd)
+ return NULL;
+
+ cmd->size = size;
+ cmd->id = id;
+ if (0 == id)
+ make_item_name (name, &(cmd->items[0]));
+
+ cmd->attach_flags = KDBUS_ATTACH_CREDS | KDBUS_ATTACH_PIDS;
+ if (get_sec_label)
+ cmd->attach_flags |= KDBUS_ATTACH_SECLABEL;
+
+ cmd->flags = 0;
+
+ return cmd;
+}
+
+/**
+ * Gets connection info for the given unique id.
+ *
+ * @param transport transport
+ * @param id unique id to query for
+ * @param get_sec_label #TRUE if sec_label field in pInfo should be filled
+ * @param pInfo nameInfo structure address to store info about the name
+ * @return 0 on success, errno if failed
+ *
+ * @note If you specify #TRUE in get_sec_label param, you must free
+ * pInfo.sec_label with dbus_free() after use.
+ */
+int
+_kdbus_connection_info_by_id (kdbus_t *kdbus,
+ dbus_uint64_t id,
+ dbus_bool_t get_sec_label,
+ struct nameInfo *pInfo)
+{
+ struct kdbus_cmd_info *cmd = prepare_connection_info_cmd (id, NULL, get_sec_label);
+
+ return process_connection_info_cmd (kdbus, cmd, pInfo, get_sec_label);
+}
+
+/**
+ * Gets connection info for the given name
+ *
+ * @param transport transport
+ * @param name name to query for
+ * @param get_sec_label #TRUE if sec_label field in pInfo should be filled
+ * @param pInfo nameInfo structure address to store info about the name
+ * @return 0 on success, errno if failed
+ *
+ * @note If you specify #TRUE in get_sec_label param, you must free
+ * pInfo.sec_label with dbus_free() after use.
+ */
+int
+_kdbus_connection_info_by_name (kdbus_t *kdbus,
+ const char *name,
+ dbus_bool_t get_sec_label,
+ struct nameInfo *pInfo)
+{
+ struct kdbus_cmd_info *cmd;
+
+ /* if name starts with ":1." it is a unique name and should be send as number */
+ if((name[0] == ':') && (name[1] == '1') && (name[2] == '.'))
+ {
+ return _kdbus_connection_info_by_id (kdbus,
+ strtoull(&name[3], NULL, 10),
+ get_sec_label,
+ pInfo);
+ }
+
+ cmd = prepare_connection_info_cmd (0, name, get_sec_label);
+
+ return process_connection_info_cmd (kdbus, cmd, pInfo, get_sec_label);
+}
+
+/**
+ * Opposing to dbus, in kdbus removes all match rules with given
+ * cookie, which in this implementation is equal to uniqe id.
+ *
+ * @param transport transport
+ * @param id connection id for which rules are to be removed
+ * @param cookie cookie of the rules to be removed
+ */
+static dbus_bool_t
+remove_match_kdbus (DBusTransport *transport,
+ __u64 cookie)
+{
+ struct kdbus_cmd_match cmd;
+ int fd;
+
+ if(!_dbus_transport_get_socket_fd(transport, &fd))
+ return FALSE;
+
+ cmd.cookie = cookie;
+ cmd.size = sizeof(struct kdbus_cmd_match);
+ cmd.flags = 0;
+
+ if(ioctl(fd, KDBUS_CMD_MATCH_REMOVE, &cmd))
+ {
+ _dbus_verbose("Failed removing match rule %llu, error: %d, %m\n", cookie, errno);
+ return FALSE;
+ }
+ else
+ {
+ _dbus_verbose("Match rule %llu removed correctly.\n", cookie);
+ return TRUE;
+ }
+}
+
+/*
+ * Removes match rule in kdbus on behalf of sender of the message
+ */
+dbus_bool_t
+kdbus_remove_match (DBusTransport *transport,
+ DBusList *rules,
+ const char *sender,
+ MatchRule *rule_to_remove,
+ DBusError *error)
+{
+ __u64 cookie = 0;
+ DBusList *link = NULL;
+
+ if (rules != NULL)
+ {
+ /* we traverse backward because bus_connection_remove_match_rule()
+ * removes the most-recently-added rule
+ */
+ link = _dbus_list_get_last_link (&rules);
+ while (link != NULL)
+ {
+ MatchRule *rule;
+ DBusList *prev;
+
+ rule = link->data;
+ prev = _dbus_list_get_prev_link (&rules, link);
+
+ if (match_rule_equal_lib (rule, rule_to_remove))
+ {
+ cookie = match_rule_get_cookie(rule);
+ break;
+ }
+
+ link = prev;
+ }
+ }
+
+ if(cookie == 0)
+ {
+ dbus_set_error (error, DBUS_ERROR_MATCH_RULE_NOT_FOUND,
+ "The given match rule wasn't found and can't be removed");
+ return FALSE;
+ }
+
+ if(!remove_match_kdbus (transport, cookie))
+ {
+ dbus_set_error (error, _dbus_error_from_errno (errno), "Could not remove match rule");
+ return FALSE;
+ }
+
+ return TRUE;
+}
diff --git a/dbus/kdbus-common.h b/dbus/kdbus-common.h
new file mode 100644
index 00000000..cea606e0
--- /dev/null
+++ b/dbus/kdbus-common.h
@@ -0,0 +1,197 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* kdbus-common.h kdbus related utils for daemon and libdbus
+ *
+ * Copyright (C) 2013 Samsung Electronics
+ *
+ * Licensed under the Academic Free License version 2.1
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version and under the terms of the GNU
+ * Lesser General Public License as published by the
+ * Free Software Foundation; either version 2.1 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifndef KDBUS_COMMON_H_
+#define KDBUS_COMMON_H_
+
+#include <dbus/dbus-types.h>
+#include <dbus/dbus-transport.h>
+#include "dbus-signals.h"
+#include "kdbus.h"
+
+#define KDBUS_ALIGN8(l) (((l) + 7) & ~7)
+
+#define KDBUS_ITEM_HEADER_SIZE offsetof(struct kdbus_item, data)
+#define KDBUS_ITEM_SIZE(s) KDBUS_ALIGN8(KDBUS_ITEM_HEADER_SIZE + (s))
+#define KDBUS_ITEM_NEXT(item) \
+ (typeof(item))(((uint8_t *)item) + KDBUS_ALIGN8((item)->size))
+#define KDBUS_ITEM_FOREACH(item, head, first) \
+ for (item = (head)->first; \
+ (uint8_t *)(item) < (uint8_t *)(head) + (head)->size; \
+ item = KDBUS_ITEM_NEXT(item))
+
+#define KDBUS_MSG_MAX_PAYLOAD_VEC_SIZE 0x00200000 /* maximum size of message header and items */
+
+struct nameInfo
+{
+ __u64 uniqueId;
+ __u64 flags;
+ __u64 userId;
+ __u64 processId;
+ __u32 sec_label_len;
+ char *sec_label;
+};
+
+typedef struct kdbus_t kdbus_t;
+
+kdbus_t * _kdbus_new (void);
+void _kdbus_free (kdbus_t *kdbus);
+
+int _kdbus_open (kdbus_t *kdbus, const char *path);
+int _kdbus_close (kdbus_t *kdbus);
+
+int _kdbus_hello (kdbus_t *kdbus,
+ dbus_uint64_t flags,
+ dbus_uint64_t attach_flags_send,
+ dbus_uint64_t attach_flags_recv,
+ dbus_uint64_t pool_size,
+ const char *activator_name,
+ const char *connection_name);
+
+int _kdbus_send (kdbus_t *kdbus,
+ dbus_uint64_t flags,
+ struct kdbus_msg *msg,
+ struct kdbus_msg **msg_reply);
+
+int _kdbus_recv (kdbus_t *kdbus,
+ dbus_uint64_t flags,
+ dbus_int64_t priority,
+ struct kdbus_msg **msg);
+
+int _kdbus_list (kdbus_t *kdbus,
+ dbus_uint64_t flags,
+ struct kdbus_info **name_list,
+ dbus_uint64_t *list_size);
+
+int _kdbus_add_match_name_change (kdbus_t *kdbus,
+ dbus_uint64_t flags,
+ dbus_uint64_t cookie,
+ dbus_uint64_t old_id,
+ dbus_uint64_t old_id_flags,
+ dbus_uint64_t new_id,
+ dbus_uint64_t new_id_flags);
+
+int _kdbus_add_match_id_change (kdbus_t *kdbus,
+ dbus_uint64_t flags,
+ dbus_uint64_t cookie,
+ dbus_uint64_t id,
+ dbus_uint64_t id_flags);
+
+int _kdbus_add_match (kdbus_t *kdbus,
+ struct kdbus_cmd_match *cmd);
+
+int _kdbus_connection_info_by_name (kdbus_t *kdbus,
+ const char *name,
+ dbus_bool_t get_sec_label,
+ struct nameInfo *pInfo);
+
+int _kdbus_connection_info_by_id (kdbus_t *kdbus,
+ dbus_uint64_t id,
+ dbus_bool_t get_sec_label,
+ struct nameInfo *pInfo);
+
+dbus_uint64_t _kdbus_compute_msg_items_size (kdbus_t *kdbus,
+ const char *destination,
+ dbus_uint64_t dst_id,
+ dbus_uint64_t body_size,
+ dbus_bool_t use_memfd,
+ int fds_count);
+
+struct kdbus_msg * _kdbus_new_msg (kdbus_t *kdbus,
+ dbus_uint64_t size_for_items,
+ dbus_uint64_t flags,
+ dbus_int64_t priority,
+ dbus_uint64_t dst_id,
+ dbus_uint64_t src_id,
+ enum kdbus_payload_type payload_type,
+ dbus_uint64_t cookie,
+ dbus_uint64_t timeout_ns_or_cookie_reply);
+
+void _kdbus_free_msg (struct kdbus_msg *msg);
+
+struct kdbus_cmd_match *_kdbus_new_cmd_match (kdbus_t *kdbus,
+ dbus_uint64_t items_size,
+ dbus_uint64_t flags,
+ dbus_uint64_t cookie);
+
+void _kdbus_free_cmd_match (struct kdbus_cmd_match *cmd);
+
+int _kdbus_free_mem (kdbus_t *kdbus, void *mem);
+
+struct kdbus_item * _kdbus_item_add_string (struct kdbus_item *item,
+ dbus_uint64_t item_type,
+ const char *item_string,
+ dbus_uint64_t item_string_size);
+
+struct kdbus_item * _kdbus_item_add_payload_memfd (struct kdbus_item *item,
+ dbus_uint64_t start,
+ dbus_uint64_t size,
+ int fd);
+
+struct kdbus_item * _kdbus_item_add_payload_vec (struct kdbus_item *item,
+ dbus_uint64_t size,
+ dbus_uint64_t address_or_offset);
+
+struct kdbus_item * _kdbus_item_add_fds (struct kdbus_item *item,
+ const int *fds,
+ int fds_count);
+
+struct kdbus_item * _kdbus_item_add_bloom_filter (struct kdbus_item *item,
+ dbus_uint64_t data_size,
+ struct kdbus_bloom_filter **out_ptr);
+
+struct kdbus_item * _kdbus_item_add_name_change (struct kdbus_item *item,
+ dbus_uint64_t old_id,
+ dbus_uint64_t old_id_flags,
+ dbus_uint64_t new_id,
+ dbus_uint64_t new_id_flags);
+
+struct kdbus_item * _kdbus_item_add_id_add (struct kdbus_item *item,
+ dbus_uint64_t id,
+ dbus_uint64_t id_flags);
+
+struct kdbus_item * _kdbus_item_add_id (struct kdbus_item *item,
+ dbus_uint64_t id);
+
+struct kdbus_item * _kdbus_item_add_bloom_mask (struct kdbus_item *item,
+ dbus_uint64_t *bloom,
+ dbus_uint64_t bloom_size);
+
+int request_kdbus_name (DBusTransport* transport, const char *name, const __u64 flags);
+int release_kdbus_name (DBusTransport* transport, const char *name);
+
+dbus_bool_t kdbus_remove_match (DBusTransport *transport, DBusList *rules, const char *sender,
+ MatchRule *rule_to_remove, DBusError *error);
+
+/** temporary accessors - to delete soon */
+int _kdbus_fd (kdbus_t *kdbus);
+void *_kdbus_mmap_ptr (kdbus_t *kdbus);
+dbus_uint64_t _kdbus_id (kdbus_t *kdbus);
+char *_kdbus_bus_id (kdbus_t *kdbus);
+dbus_uint64_t _kdbus_bus_id_size (void);
+struct kdbus_bloom_parameter *_kdbus_bloom (kdbus_t *kdbus);
+
+#endif /* KDBUS_COMMON_H_ */
diff --git a/dbus/kdbus.h b/dbus/kdbus.h
new file mode 100644
index 00000000..4fc44cb1
--- /dev/null
+++ b/dbus/kdbus.h
@@ -0,0 +1,984 @@
+/*
+ * kdbus is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the
+ * Free Software Foundation; either version 2.1 of the License, or (at
+ * your option) any later version.
+ */
+
+#ifndef _UAPI_KDBUS_H_
+#define _UAPI_KDBUS_H_
+
+#include <linux/ioctl.h>
+#include <linux/types.h>
+
+#define KDBUS_IOCTL_MAGIC 0x95
+#define KDBUS_SRC_ID_KERNEL (0)
+#define KDBUS_DST_ID_NAME (0)
+#define KDBUS_MATCH_ID_ANY (~0ULL)
+#define KDBUS_DST_ID_BROADCAST (~0ULL)
+#define KDBUS_FLAG_NEGOTIATE (1ULL << 63)
+
+/**
+ * struct kdbus_notify_id_change - name registry change message
+ * @id: New or former owner of the name
+ * @flags: flags field from KDBUS_HELLO_*
+ *
+ * Sent from kernel to userspace when the owner or activator of
+ * a well-known name changes.
+ *
+ * Attached to:
+ * KDBUS_ITEM_ID_ADD
+ * KDBUS_ITEM_ID_REMOVE
+ */
+struct kdbus_notify_id_change {
+ __u64 id;
+ __u64 flags;
+} __attribute__((__aligned__(8)));
+
+/**
+ * struct kdbus_notify_name_change - name registry change message
+ * @old_id: ID and flags of former owner of a name
+ * @new_id: ID and flags of new owner of a name
+ * @name: Well-known name
+ *
+ * Sent from kernel to userspace when the owner or activator of
+ * a well-known name changes.
+ *
+ * Attached to:
+ * KDBUS_ITEM_NAME_ADD
+ * KDBUS_ITEM_NAME_REMOVE
+ * KDBUS_ITEM_NAME_CHANGE
+ */
+struct kdbus_notify_name_change {
+ struct kdbus_notify_id_change old_id;
+ struct kdbus_notify_id_change new_id;
+ char name[0];
+} __attribute__((__aligned__(8)));
+
+/**
+ * struct kdbus_creds - process credentials
+ * @uid: User ID
+ * @euid: Effective UID
+ * @suid: Saved UID
+ * @fsuid: Filesystem UID
+ * @gid: Group ID
+ * @egid: Effective GID
+ * @sgid: Saved GID
+ * @fsgid: Filesystem GID
+ *
+ * Attached to:
+ * KDBUS_ITEM_CREDS
+ */
+struct kdbus_creds {
+ __u64 uid;
+ __u64 euid;
+ __u64 suid;
+ __u64 fsuid;
+ __u64 gid;
+ __u64 egid;
+ __u64 sgid;
+ __u64 fsgid;
+} __attribute__((__aligned__(8)));
+
+/**
+ * struct kdbus_pids - process identifiers
+ * @pid: Process ID
+ * @tid: Thread ID
+ * @ppid: Parent process ID
+ *
+ * The PID and TID of a process.
+ *
+ * Attached to:
+ * KDBUS_ITEM_PIDS
+ */
+struct kdbus_pids {
+ __u64 pid;
+ __u64 tid;
+ __u64 ppid;
+} __attribute__((__aligned__(8)));
+
+/**
+ * struct kdbus_caps - process capabilities
+ * @last_cap: Highest currently known capability bit
+ * @caps: Variable number of 32-bit capabilities flags
+ *
+ * Contains a variable number of 32-bit capabilities flags.
+ *
+ * Attached to:
+ * KDBUS_ITEM_CAPS
+ */
+struct kdbus_caps {
+ __u32 last_cap;
+ __u32 caps[0];
+} __attribute__((__aligned__(8)));
+
+/**
+ * struct kdbus_audit - audit information
+ * @sessionid: The audit session ID
+ * @loginuid: The audit login uid
+ *
+ * Attached to:
+ * KDBUS_ITEM_AUDIT
+ */
+struct kdbus_audit {
+ __u32 sessionid;
+ __u32 loginuid;
+} __attribute__((__aligned__(8)));
+
+/**
+ * struct kdbus_timestamp
+ * @seqnum: Global per-domain message sequence number
+ * @monotonic_ns: Monotonic timestamp, in nanoseconds
+ * @realtime_ns: Realtime timestamp, in nanoseconds
+ *
+ * Attached to:
+ * KDBUS_ITEM_TIMESTAMP
+ */
+struct kdbus_timestamp {
+ __u64 seqnum;
+ __u64 monotonic_ns;
+ __u64 realtime_ns;
+} __attribute__((__aligned__(8)));
+
+/**
+ * struct kdbus_vec - I/O vector for kdbus payload items
+ * @size: The size of the vector
+ * @address: Memory address of data buffer
+ * @offset: Offset in the in-message payload memory,
+ * relative to the message head
+ *
+ * Attached to:
+ * KDBUS_ITEM_PAYLOAD_VEC, KDBUS_ITEM_PAYLOAD_OFF
+ */
+struct kdbus_vec {
+ __u64 size;
+ union {
+ __u64 address;
+ __u64 offset;
+ };
+} __attribute__((__aligned__(8)));
+
+/**
+ * struct kdbus_bloom_parameter - bus-wide bloom parameters
+ * @size: Size of the bit field in bytes (m / 8)
+ * @n_hash: Number of hash functions used (k)
+ */
+struct kdbus_bloom_parameter {
+ __u64 size;
+ __u64 n_hash;
+} __attribute__((__aligned__(8)));
+
+/**
+ * struct kdbus_bloom_filter - bloom filter containing n elements
+ * @generation: Generation of the element set in the filter
+ * @data: Bit field, multiple of 8 bytes
+ */
+struct kdbus_bloom_filter {
+ __u64 generation;
+ __u64 data[0];
+} __attribute__((__aligned__(8)));
+
+/**
+ * struct kdbus_memfd - a kdbus memfd
+ * @start: The offset into the memfd where the segment starts
+ * @size: The size of the memfd segment
+ * @fd: The file descriptor number
+ * @__pad: Padding to ensure proper alignment and size
+ *
+ * Attached to:
+ * KDBUS_ITEM_PAYLOAD_MEMFD
+ */
+struct kdbus_memfd {
+ __u64 start;
+ __u64 size;
+ int fd;
+ __u32 __pad;
+} __attribute__((__aligned__(8)));
+
+/**
+ * struct kdbus_name - a registered well-known name with its flags
+ * @flags: Flags from KDBUS_NAME_*
+ * @name: Well-known name
+ *
+ * Attached to:
+ * KDBUS_ITEM_OWNED_NAME
+ */
+struct kdbus_name {
+ __u64 flags;
+ char name[0];
+} __attribute__((__aligned__(8)));
+
+/**
+ * enum kdbus_policy_access_type - permissions of a policy record
+ * @_KDBUS_POLICY_ACCESS_NULL: Uninitialized/invalid
+ * @KDBUS_POLICY_ACCESS_USER: Grant access to a uid
+ * @KDBUS_POLICY_ACCESS_GROUP: Grant access to gid
+ * @KDBUS_POLICY_ACCESS_WORLD: World-accessible
+ */
+enum kdbus_policy_access_type {
+ _KDBUS_POLICY_ACCESS_NULL,
+ KDBUS_POLICY_ACCESS_USER,
+ KDBUS_POLICY_ACCESS_GROUP,
+ KDBUS_POLICY_ACCESS_WORLD,
+};
+
+/**
+ * enum kdbus_policy_access_flags - mode flags
+ * @KDBUS_POLICY_OWN: Allow to own a well-known name
+ * Implies KDBUS_POLICY_TALK and KDBUS_POLICY_SEE
+ * @KDBUS_POLICY_TALK: Allow communication to a well-known name
+ * Implies KDBUS_POLICY_SEE
+ * @KDBUS_POLICY_SEE: Allow to see a well-known name
+ */
+enum kdbus_policy_type {
+ KDBUS_POLICY_SEE = 0,
+ KDBUS_POLICY_TALK,
+ KDBUS_POLICY_OWN,
+};
+
+/**
+ * struct kdbus_policy_access - policy access item
+ * @type: One of KDBUS_POLICY_ACCESS_* types
+ * @access: Access to grant
+ * @id: For KDBUS_POLICY_ACCESS_USER, the uid
+ * For KDBUS_POLICY_ACCESS_GROUP, the gid
+ */
+struct kdbus_policy_access {
+ __u64 type; /* USER, GROUP, WORLD */
+ __u64 access; /* OWN, TALK, SEE */
+ __u64 id; /* uid, gid, 0 */
+} __attribute__((__aligned__(8)));
+
+/**
+ * enum kdbus_attach_flags - flags for metadata attachments
+ * @KDBUS_ATTACH_TIMESTAMP: Timestamp
+ * @KDBUS_ATTACH_CREDS: Credentials
+ * @KDBUS_ATTACH_PIDS: PIDs
+ * @KDBUS_ATTACH_AUXGROUPS: Auxiliary groups
+ * @KDBUS_ATTACH_NAMES: Well-known names
+ * @KDBUS_ATTACH_TID_COMM: The "comm" process identifier of the TID
+ * @KDBUS_ATTACH_PID_COMM: The "comm" process identifier of the PID
+ * @KDBUS_ATTACH_EXE: The path of the executable
+ * @KDBUS_ATTACH_CMDLINE: The process command line
+ * @KDBUS_ATTACH_CGROUP: The croup membership
+ * @KDBUS_ATTACH_CAPS: The process capabilities
+ * @KDBUS_ATTACH_SECLABEL: The security label
+ * @KDBUS_ATTACH_AUDIT: The audit IDs
+ * @KDBUS_ATTACH_CONN_DESCRIPTION: The human-readable connection name
+ * @_KDBUS_ATTACH_ALL: All of the above
+ * @_KDBUS_ATTACH_ANY: Wildcard match to enable any kind of
+ * metatdata.
+ */
+enum kdbus_attach_flags {
+ KDBUS_ATTACH_TIMESTAMP = 1ULL << 0,
+ KDBUS_ATTACH_CREDS = 1ULL << 1,
+ KDBUS_ATTACH_PIDS = 1ULL << 2,
+ KDBUS_ATTACH_AUXGROUPS = 1ULL << 3,
+ KDBUS_ATTACH_NAMES = 1ULL << 4,
+ KDBUS_ATTACH_TID_COMM = 1ULL << 5,
+ KDBUS_ATTACH_PID_COMM = 1ULL << 6,
+ KDBUS_ATTACH_EXE = 1ULL << 7,
+ KDBUS_ATTACH_CMDLINE = 1ULL << 8,
+ KDBUS_ATTACH_CGROUP = 1ULL << 9,
+ KDBUS_ATTACH_CAPS = 1ULL << 10,
+ KDBUS_ATTACH_SECLABEL = 1ULL << 11,
+ KDBUS_ATTACH_AUDIT = 1ULL << 12,
+ KDBUS_ATTACH_CONN_DESCRIPTION = 1ULL << 13,
+ _KDBUS_ATTACH_ALL = (1ULL << 14) - 1,
+ _KDBUS_ATTACH_ANY = ~0ULL
+};
+
+/**
+ * enum kdbus_item_type - item types to chain data in a list
+ * @_KDBUS_ITEM_NULL: Uninitialized/invalid
+ * @_KDBUS_ITEM_USER_BASE: Start of user items
+ * @KDBUS_ITEM_NEGOTIATE: Negotiate supported items
+ * @KDBUS_ITEM_PAYLOAD_VEC: Vector to data
+ * @KDBUS_ITEM_PAYLOAD_OFF: Data at returned offset to message head
+ * @KDBUS_ITEM_PAYLOAD_MEMFD: Data as sealed memfd
+ * @KDBUS_ITEM_FDS: Attached file descriptors
+ * @KDBUS_ITEM_CANCEL_FD: FD used to cancel a synchronous
+ * operation by writing to it from
+ * userspace
+ * @KDBUS_ITEM_BLOOM_PARAMETER: Bus-wide bloom parameters, used with
+ * KDBUS_CMD_BUS_MAKE, carries a
+ * struct kdbus_bloom_parameter
+ * @KDBUS_ITEM_BLOOM_FILTER: Bloom filter carried with a message,
+ * used to match against a bloom mask of a
+ * connection, carries a struct
+ * kdbus_bloom_filter
+ * @KDBUS_ITEM_BLOOM_MASK: Bloom mask used to match against a
+ * message'sbloom filter
+ * @KDBUS_ITEM_DST_NAME: Destination's well-known name
+ * @KDBUS_ITEM_MAKE_NAME: Name of domain, bus, endpoint
+ * @KDBUS_ITEM_ATTACH_FLAGS_SEND: Attach-flags, used for updating which
+ * metadata a connection opts in to send
+ * @KDBUS_ITEM_ATTACH_FLAGS_RECV: Attach-flags, used for updating which
+ * metadata a connection requests to
+ * receive for each reeceived message
+ * @KDBUS_ITEM_ID: Connection ID
+ * @KDBUS_ITEM_NAME: Well-know name with flags
+ * @_KDBUS_ITEM_ATTACH_BASE: Start of metadata attach items
+ * @KDBUS_ITEM_TIMESTAMP: Timestamp
+ * @KDBUS_ITEM_CREDS: Process credentials
+ * @KDBUS_ITEM_PIDS: Process identifiers
+ * @KDBUS_ITEM_AUXGROUPS: Auxiliary process groups
+ * @KDBUS_ITEM_OWNED_NAME: A name owned by the associated
+ * connection
+ * @KDBUS_ITEM_TID_COMM: Thread ID "comm" identifier
+ * (Don't trust this, see below.)
+ * @KDBUS_ITEM_PID_COMM: Process ID "comm" identifier
+ * (Don't trust this, see below.)
+ * @KDBUS_ITEM_EXE: The path of the executable
+ * (Don't trust this, see below.)
+ * @KDBUS_ITEM_CMDLINE: The process command line
+ * (Don't trust this, see below.)
+ * @KDBUS_ITEM_CGROUP: The croup membership
+ * @KDBUS_ITEM_CAPS: The process capabilities
+ * @KDBUS_ITEM_SECLABEL: The security label
+ * @KDBUS_ITEM_AUDIT: The audit IDs
+ * @KDBUS_ITEM_CONN_DESCRIPTION: The connection's human-readable name
+ * (debugging)
+ * @_KDBUS_ITEM_POLICY_BASE: Start of policy items
+ * @KDBUS_ITEM_POLICY_ACCESS: Policy access block
+ * @_KDBUS_ITEM_KERNEL_BASE: Start of kernel-generated message items
+ * @KDBUS_ITEM_NAME_ADD: Notification in kdbus_notify_name_change
+ * @KDBUS_ITEM_NAME_REMOVE: Notification in kdbus_notify_name_change
+ * @KDBUS_ITEM_NAME_CHANGE: Notification in kdbus_notify_name_change
+ * @KDBUS_ITEM_ID_ADD: Notification in kdbus_notify_id_change
+ * @KDBUS_ITEM_ID_REMOVE: Notification in kdbus_notify_id_change
+ * @KDBUS_ITEM_REPLY_TIMEOUT: Timeout has been reached
+ * @KDBUS_ITEM_REPLY_DEAD: Destination died
+ *
+ * N.B: The process and thread COMM fields, as well as the CMDLINE and
+ * EXE fields may be altered by unprivileged processes und should
+ * hence *not* used for security decisions. Peers should make use of
+ * these items only for informational purposes, such as generating log
+ * records.
+ */
+enum kdbus_item_type {
+ _KDBUS_ITEM_NULL,
+ _KDBUS_ITEM_USER_BASE,
+ KDBUS_ITEM_NEGOTIATE = _KDBUS_ITEM_USER_BASE,
+ KDBUS_ITEM_PAYLOAD_VEC,
+ KDBUS_ITEM_PAYLOAD_OFF,
+ KDBUS_ITEM_PAYLOAD_MEMFD,
+ KDBUS_ITEM_FDS,
+ KDBUS_ITEM_CANCEL_FD,
+ KDBUS_ITEM_BLOOM_PARAMETER,
+ KDBUS_ITEM_BLOOM_FILTER,
+ KDBUS_ITEM_BLOOM_MASK,
+ KDBUS_ITEM_DST_NAME,
+ KDBUS_ITEM_MAKE_NAME,
+ KDBUS_ITEM_ATTACH_FLAGS_SEND,
+ KDBUS_ITEM_ATTACH_FLAGS_RECV,
+ KDBUS_ITEM_ID,
+ KDBUS_ITEM_NAME,
+ KDBUS_ITEM_DST_ID,
+
+ /* keep these item types in sync with KDBUS_ATTACH_* flags */
+ _KDBUS_ITEM_ATTACH_BASE = 0x1000,
+ KDBUS_ITEM_TIMESTAMP = _KDBUS_ITEM_ATTACH_BASE,
+ KDBUS_ITEM_CREDS,
+ KDBUS_ITEM_PIDS,
+ KDBUS_ITEM_AUXGROUPS,
+ KDBUS_ITEM_OWNED_NAME,
+ KDBUS_ITEM_TID_COMM,
+ KDBUS_ITEM_PID_COMM,
+ KDBUS_ITEM_EXE,
+ KDBUS_ITEM_CMDLINE,
+ KDBUS_ITEM_CGROUP,
+ KDBUS_ITEM_CAPS,
+ KDBUS_ITEM_SECLABEL,
+ KDBUS_ITEM_AUDIT,
+ KDBUS_ITEM_CONN_DESCRIPTION,
+
+ _KDBUS_ITEM_POLICY_BASE = 0x2000,
+ KDBUS_ITEM_POLICY_ACCESS = _KDBUS_ITEM_POLICY_BASE,
+
+ _KDBUS_ITEM_KERNEL_BASE = 0x8000,
+ KDBUS_ITEM_NAME_ADD = _KDBUS_ITEM_KERNEL_BASE,
+ KDBUS_ITEM_NAME_REMOVE,
+ KDBUS_ITEM_NAME_CHANGE,
+ KDBUS_ITEM_ID_ADD,
+ KDBUS_ITEM_ID_REMOVE,
+ KDBUS_ITEM_REPLY_TIMEOUT,
+ KDBUS_ITEM_REPLY_DEAD,
+};
+
+/**
+ * struct kdbus_item - chain of data blocks
+ * @size: Overall data record size
+ * @type: Kdbus_item type of data
+ * @data: Generic bytes
+ * @data32: Generic 32 bit array
+ * @data64: Generic 64 bit array
+ * @str: Generic string
+ * @id: Connection ID
+ * @vec: KDBUS_ITEM_PAYLOAD_VEC
+ * @creds: KDBUS_ITEM_CREDS
+ * @audit: KDBUS_ITEM_AUDIT
+ * @timestamp: KDBUS_ITEM_TIMESTAMP
+ * @name: KDBUS_ITEM_NAME
+ * @bloom_parameter: KDBUS_ITEM_BLOOM_PARAMETER
+ * @bloom_filter: KDBUS_ITEM_BLOOM_FILTER
+ * @memfd: KDBUS_ITEM_PAYLOAD_MEMFD
+ * @name_change: KDBUS_ITEM_NAME_ADD
+ * KDBUS_ITEM_NAME_REMOVE
+ * KDBUS_ITEM_NAME_CHANGE
+ * @id_change: KDBUS_ITEM_ID_ADD
+ * KDBUS_ITEM_ID_REMOVE
+ * @policy: KDBUS_ITEM_POLICY_ACCESS
+ */
+struct kdbus_item {
+ __u64 size;
+ __u64 type;
+ union {
+ __u8 data[0];
+ __u32 data32[0];
+ __u64 data64[0];
+ char str[0];
+
+ __u64 id;
+ struct kdbus_vec vec;
+ struct kdbus_creds creds;
+ struct kdbus_pids pids;
+ struct kdbus_audit audit;
+ struct kdbus_caps caps;
+ struct kdbus_timestamp timestamp;
+ struct kdbus_name name;
+ struct kdbus_bloom_parameter bloom_parameter;
+ struct kdbus_bloom_filter bloom_filter;
+ struct kdbus_memfd memfd;
+ int fds[0];
+ struct kdbus_notify_name_change name_change;
+ struct kdbus_notify_id_change id_change;
+ struct kdbus_policy_access policy_access;
+ };
+} __attribute__((__aligned__(8)));
+
+/**
+ * enum kdbus_msg_flags - type of message
+ * @KDBUS_MSG_EXPECT_REPLY: Expect a reply message, used for
+ * method calls. The userspace-supplied
+ * cookie identifies the message and the
+ * respective reply carries the cookie
+ * in cookie_reply
+ * @KDBUS_MSG_NO_AUTO_START: Do not start a service if the addressed
+ * name is not currently active. This flag is
+ * not looked at by the kernel but only
+ * serves as hint for userspace implementations.
+ * @KDBUS_MSG_SIGNAL: Treat this message as signal
+ */
+enum kdbus_msg_flags {
+ KDBUS_MSG_EXPECT_REPLY = 1ULL << 0,
+ KDBUS_MSG_NO_AUTO_START = 1ULL << 1,
+ KDBUS_MSG_SIGNAL = 1ULL << 2,
+};
+
+/**
+ * enum kdbus_payload_type - type of payload carried by message
+ * @KDBUS_PAYLOAD_KERNEL: Kernel-generated simple message
+ * @KDBUS_PAYLOAD_DBUS: D-Bus marshalling "DBusDBus"
+ *
+ * Any payload-type is accepted. Common types will get added here once
+ * established.
+ */
+enum kdbus_payload_type {
+ KDBUS_PAYLOAD_KERNEL,
+ KDBUS_PAYLOAD_DBUS = 0x4442757344427573ULL,
+};
+
+/**
+ * struct kdbus_msg - the representation of a kdbus message
+ * @size: Total size of the message
+ * @flags: Message flags (KDBUS_MSG_*), userspace → kernel
+ * @priority: Message queue priority value
+ * @dst_id: 64-bit ID of the destination connection
+ * @src_id: 64-bit ID of the source connection
+ * @payload_type: Payload type (KDBUS_PAYLOAD_*)
+ * @cookie: Userspace-supplied cookie, for the connection
+ * to identify its messages
+ * @timeout_ns: The time to wait for a message reply from the peer.
+ * If there is no reply, and the send command is
+ * executed asynchronously, a kernel-generated message
+ * with an attached KDBUS_ITEM_REPLY_TIMEOUT item
+ * is sent to @src_id. For synchronously executed send
+ * command, the value denotes the maximum time the call
+ * blocks to wait for a reply. The timeout is expected in
+ * nanoseconds and as absolute CLOCK_MONOTONIC value.
+ * @cookie_reply: A reply to the requesting message with the same
+ * cookie. The requesting connection can match its
+ * request and the reply with this value
+ * @items: A list of kdbus_items containing the message payload
+ */
+struct kdbus_msg {
+ __u64 size;
+ __u64 flags;
+ __s64 priority;
+ __u64 dst_id;
+ __u64 src_id;
+ __u64 payload_type;
+ __u64 cookie;
+ union {
+ __u64 timeout_ns;
+ __u64 cookie_reply;
+ };
+ struct kdbus_item items[0];
+} __attribute__((__aligned__(8)));
+
+/**
+ * struct kdbus_msg_info - returned message container
+ * @offset: Offset of kdbus_msg slice in pool
+ * @msg_size: Copy of the kdbus_msg.size field
+ * @return_flags: Command return flags, kernel → userspace
+ */
+struct kdbus_msg_info {
+ __u64 offset;
+ __u64 msg_size;
+ __u64 return_flags;
+} __attribute__((__aligned__(8)));
+
+/**
+ * enum kdbus_send_flags - flags for sending messages
+ * @KDBUS_SEND_SYNC_REPLY: Wait for destination connection to
+ * reply to this message. The
+ * KDBUS_CMD_SEND ioctl() will block
+ * until the reply is received, and
+ * reply in struct kdbus_cmd_send will
+ * yield the offset in the sender's pool
+ * where the reply can be found.
+ * This flag is only valid if
+ * @KDBUS_MSG_EXPECT_REPLY is set as well.
+ */
+enum kdbus_send_flags {
+ KDBUS_SEND_SYNC_REPLY = 1ULL << 0,
+};
+
+/**
+ * struct kdbus_cmd_send - send message
+ * @size: Overall size of this structure
+ * @flags: Flags to change send behavior (KDBUS_SEND_*)
+ * @return_flags: Command return flags, kernel → userspace
+ * @msg_address: Storage address of the kdbus_msg to send
+ * @reply: Storage for message reply if KDBUS_SEND_SYNC_REPLY
+ * was given
+ * @items: Additional items for this command
+ */
+struct kdbus_cmd_send {
+ __u64 size;
+ __u64 flags;
+ __u64 return_flags;
+ __u64 msg_address;
+ struct kdbus_msg_info reply;
+ struct kdbus_item items[0];
+} __attribute__((__aligned__(8)));
+
+/**
+ * enum kdbus_recv_flags - flags for de-queuing messages
+ * @KDBUS_RECV_PEEK: Return the next queued message without
+ * actually de-queuing it, and without installing
+ * any file descriptors or other resources. It is
+ * usually used to determine the activating
+ * connection of a bus name.
+ * @KDBUS_RECV_DROP: Drop and free the next queued message and all
+ * its resources without actually receiving it.
+ * @KDBUS_RECV_USE_PRIORITY: Only de-queue messages with the specified or
+ * higher priority (lowest values); if not set,
+ * the priority value is ignored.
+ */
+enum kdbus_recv_flags {
+ KDBUS_RECV_PEEK = 1ULL << 0,
+ KDBUS_RECV_DROP = 1ULL << 1,
+ KDBUS_RECV_USE_PRIORITY = 1ULL << 2,
+};
+
+/**
+ * enum kdbus_recv_return_flags - return flags for message receive commands
+ * @KDBUS_RECV_RETURN_INCOMPLETE_FDS: One or more file descriptors could not
+ * be installed. These descriptors in
+ * KDBUS_ITEM_FDS will carry the value -1.
+ * @KDBUS_RECV_RETURN_DROPPED_MSGS: There have been dropped messages since
+ * the last time a message was received.
+ * The 'dropped_msgs' counter contains the
+ * number of messages dropped pool
+ * overflows or other missed broadcasts.
+ */
+enum kdbus_recv_return_flags {
+ KDBUS_RECV_RETURN_INCOMPLETE_FDS = 1ULL << 0,
+ KDBUS_RECV_RETURN_DROPPED_MSGS = 1ULL << 1,
+};
+
+/**
+ * struct kdbus_cmd_recv - struct to de-queue a buffered message
+ * @size: Overall size of this object
+ * @flags: KDBUS_RECV_* flags, userspace → kernel
+ * @return_flags: Command return flags, kernel → userspace
+ * @priority: Minimum priority of the messages to de-queue. Lowest
+ * values have the highest priority.
+ * @dropped_msgs: In case there were any dropped messages since the last
+ * time a message was received, this will be set to the
+ * number of lost messages and
+ * KDBUS_RECV_RETURN_DROPPED_MSGS will be set in
+ * 'return_flags'. This can only happen if the ioctl
+ * returns 0 or EAGAIN.
+ * @msg: Return storage for received message.
+ * @items: Additional items for this command.
+ *
+ * This struct is used with the KDBUS_CMD_RECV ioctl.
+ */
+struct kdbus_cmd_recv {
+ __u64 size;
+ __u64 flags;
+ __u64 return_flags;
+ __s64 priority;
+ __u64 dropped_msgs;
+ struct kdbus_msg_info msg;
+ struct kdbus_item items[0];
+} __attribute__((__aligned__(8)));
+
+/**
+ * struct kdbus_cmd_free - struct to free a slice of memory in the pool
+ * @size: Overall size of this structure
+ * @flags: Flags for the free command, userspace → kernel
+ * @return_flags: Command return flags, kernel → userspace
+ * @offset: The offset of the memory slice, as returned by other
+ * ioctls
+ * @items: Additional items to modify the behavior
+ *
+ * This struct is used with the KDBUS_CMD_FREE ioctl.
+ */
+struct kdbus_cmd_free {
+ __u64 size;
+ __u64 flags;
+ __u64 return_flags;
+ __u64 offset;
+ struct kdbus_item items[0];
+} __attribute__((__aligned__(8)));
+
+/**
+ * enum kdbus_hello_flags - flags for struct kdbus_cmd_hello
+ * @KDBUS_HELLO_ACCEPT_FD: The connection allows the reception of
+ * any passed file descriptors
+ * @KDBUS_HELLO_ACTIVATOR: Special-purpose connection which registers
+ * a well-know name for a process to be started
+ * when traffic arrives
+ * @KDBUS_HELLO_POLICY_HOLDER: Special-purpose connection which registers
+ * policy entries for a name. The provided name
+ * is not activated and not registered with the
+ * name database, it only allows unprivileged
+ * connections to acquire a name, talk or discover
+ * a service
+ * @KDBUS_HELLO_MONITOR: Special-purpose connection to monitor
+ * bus traffic
+ */
+enum kdbus_hello_flags {
+ KDBUS_HELLO_ACCEPT_FD = 1ULL << 0,
+ KDBUS_HELLO_ACTIVATOR = 1ULL << 1,
+ KDBUS_HELLO_POLICY_HOLDER = 1ULL << 2,
+ KDBUS_HELLO_MONITOR = 1ULL << 3,
+};
+
+/**
+ * struct kdbus_cmd_hello - struct to say hello to kdbus
+ * @size: The total size of the structure
+ * @flags: Connection flags (KDBUS_HELLO_*), userspace → kernel
+ * @return_flags: Command return flags, kernel → userspace
+ * @attach_flags_send: Mask of metadata to attach to each message sent
+ * off by this connection (KDBUS_ATTACH_*)
+ * @attach_flags_recv: Mask of metadata to attach to each message receieved
+ * by the new connection (KDBUS_ATTACH_*)
+ * @bus_flags: The flags field copied verbatim from the original
+ * KDBUS_CMD_BUS_MAKE ioctl. It's intended to be useful
+ * to do negotiation of features of the payload that is
+ * transferred (kernel → userspace)
+ * @id: The ID of this connection (kernel → userspace)
+ * @pool_size: Size of the connection's buffer where the received
+ * messages are placed
+ * @offset: Pool offset where items are returned to report
+ * additional information about the bus and the newly
+ * created connection.
+ * @items_size: Size of buffer returned in the pool slice at @offset.
+ * @id128: Unique 128-bit ID of the bus (kernel → userspace)
+ * @items: A list of items
+ *
+ * This struct is used with the KDBUS_CMD_HELLO ioctl.
+ */
+struct kdbus_cmd_hello {
+ __u64 size;
+ __u64 flags;
+ __u64 return_flags;
+ __u64 attach_flags_send;
+ __u64 attach_flags_recv;
+ __u64 bus_flags;
+ __u64 id;
+ __u64 pool_size;
+ __u64 offset;
+ __u64 items_size;
+ __u8 id128[16];
+ struct kdbus_item items[0];
+} __attribute__((__aligned__(8)));
+
+/**
+ * struct kdbus_info - connection information
+ * @size: total size of the struct
+ * @id: 64bit object ID
+ * @flags: object creation flags
+ * @items: list of items
+ *
+ * Note that the user is responsible for freeing the allocated memory with
+ * the KDBUS_CMD_FREE ioctl.
+ */
+struct kdbus_info {
+ __u64 size;
+ __u64 id;
+ __u64 flags;
+ struct kdbus_item items[0];
+} __attribute__((__aligned__(8)));
+
+/**
+ * enum kdbus_list_flags - what to include into the returned list
+ * @KDBUS_LIST_UNIQUE: active connections
+ * @KDBUS_LIST_ACTIVATORS: activator connections
+ * @KDBUS_LIST_NAMES: known well-known names
+ * @KDBUS_LIST_QUEUED: queued-up names
+ */
+enum kdbus_list_flags {
+ KDBUS_LIST_UNIQUE = 1ULL << 0,
+ KDBUS_LIST_NAMES = 1ULL << 1,
+ KDBUS_LIST_ACTIVATORS = 1ULL << 2,
+ KDBUS_LIST_QUEUED = 1ULL << 3,
+};
+
+/**
+ * struct kdbus_cmd_list - list connections
+ * @size: overall size of this object
+ * @flags: flags for the query (KDBUS_LIST_*), userspace → kernel
+ * @return_flags: command return flags, kernel → userspace
+ * @offset: Offset in the caller's pool buffer where an array of
+ * kdbus_info objects is stored.
+ * The user must use KDBUS_CMD_FREE to free the
+ * allocated memory.
+ * @list_size: size of returned list in bytes
+ * @items: Items for the command. Reserved for future use.
+ *
+ * This structure is used with the KDBUS_CMD_LIST ioctl.
+ */
+struct kdbus_cmd_list {
+ __u64 size;
+ __u64 flags;
+ __u64 return_flags;
+ __u64 offset;
+ __u64 list_size;
+ struct kdbus_item items[0];
+} __attribute__((__aligned__(8)));
+
+/**
+ * struct kdbus_cmd_info - struct used for KDBUS_CMD_CONN_INFO ioctl
+ * @size: The total size of the struct
+ * @flags: Flags for this ioctl, userspace → kernel
+ * @return_flags: Command return flags, kernel → userspace
+ * @id: The 64-bit ID of the connection. If set to zero, passing
+ * @name is required. kdbus will look up the name to
+ * determine the ID in this case.
+ * @attach_flags: Set of attach flags to specify the set of information
+ * to receive, userspace → kernel
+ * @offset: Returned offset in the caller's pool buffer where the
+ * kdbus_info struct result is stored. The user must
+ * use KDBUS_CMD_FREE to free the allocated memory.
+ * @info_size: Output buffer to report size of data at @offset.
+ * @items: The optional item list, containing the
+ * well-known name to look up as a KDBUS_ITEM_NAME.
+ * Only needed in case @id is zero.
+ *
+ * On success, the KDBUS_CMD_CONN_INFO ioctl will return 0 and @offset will
+ * tell the user the offset in the connection pool buffer at which to find the
+ * result in a struct kdbus_info.
+ */
+struct kdbus_cmd_info {
+ __u64 size;
+ __u64 flags;
+ __u64 return_flags;
+ __u64 id;
+ __u64 attach_flags;
+ __u64 offset;
+ __u64 info_size;
+ struct kdbus_item items[0];
+} __attribute__((__aligned__(8)));
+
+/**
+ * enum kdbus_cmd_match_flags - flags to control the KDBUS_CMD_MATCH_ADD ioctl
+ * @KDBUS_MATCH_REPLACE: If entries with the supplied cookie already
+ * exists, remove them before installing the new
+ * matches.
+ */
+enum kdbus_cmd_match_flags {
+ KDBUS_MATCH_REPLACE = 1ULL << 0,
+};
+
+/**
+ * struct kdbus_cmd_match - struct to add or remove matches
+ * @size: The total size of the struct
+ * @flags: Flags for match command (KDBUS_MATCH_*),
+ * userspace → kernel
+ * @return_flags: Command return flags, kernel → userspace
+ * @cookie: Userspace supplied cookie. When removing, the cookie
+ * identifies the match to remove
+ * @items: A list of items for additional information
+ *
+ * This structure is used with the KDBUS_CMD_MATCH_ADD and
+ * KDBUS_CMD_MATCH_REMOVE ioctl.
+ */
+struct kdbus_cmd_match {
+ __u64 size;
+ __u64 flags;
+ __u64 return_flags;
+ __u64 cookie;
+ struct kdbus_item items[0];
+} __attribute__((__aligned__(8)));
+
+/**
+ * enum kdbus_make_flags - Flags for KDBUS_CMD_{BUS,ENDPOINT}_MAKE
+ * @KDBUS_MAKE_ACCESS_GROUP: Make the bus or endpoint node group-accessible
+ * @KDBUS_MAKE_ACCESS_WORLD: Make the bus or endpoint node world-accessible
+ */
+enum kdbus_make_flags {
+ KDBUS_MAKE_ACCESS_GROUP = 1ULL << 0,
+ KDBUS_MAKE_ACCESS_WORLD = 1ULL << 1,
+};
+
+/**
+ * enum kdbus_name_flags - flags for KDBUS_CMD_NAME_ACQUIRE
+ * @KDBUS_NAME_REPLACE_EXISTING: Try to replace name of other connections
+ * @KDBUS_NAME_ALLOW_REPLACEMENT: Allow the replacement of the name
+ * @KDBUS_NAME_QUEUE: Name should be queued if busy
+ * @KDBUS_NAME_IN_QUEUE: Name is queued
+ * @KDBUS_NAME_ACTIVATOR: Name is owned by a activator connection
+ * @KDBUS_NAME_PRIMARY: Primary owner of the name
+ * @KDBUS_NAME_ACQUIRED: Name was acquired/queued _now_
+ */
+enum kdbus_name_flags {
+ KDBUS_NAME_REPLACE_EXISTING = 1ULL << 0,
+ KDBUS_NAME_ALLOW_REPLACEMENT = 1ULL << 1,
+ KDBUS_NAME_QUEUE = 1ULL << 2,
+ KDBUS_NAME_IN_QUEUE = 1ULL << 3,
+ KDBUS_NAME_ACTIVATOR = 1ULL << 4,
+ KDBUS_NAME_PRIMARY = 1ULL << 5,
+ KDBUS_NAME_ACQUIRED = 1ULL << 6,
+};
+
+/**
+ * struct kdbus_cmd - generic ioctl payload
+ * @size: Overall size of this structure
+ * @flags: Flags for this ioctl, userspace → kernel
+ * @return_flags: Ioctl return flags, kernel → userspace
+ * @items: Additional items to modify the behavior
+ *
+ * This is a generic ioctl payload object. It's used by all ioctls that only
+ * take flags and items as input.
+ */
+struct kdbus_cmd {
+ __u64 size;
+ __u64 flags;
+ __u64 return_flags;
+ struct kdbus_item items[0];
+} __attribute__((__aligned__(8)));
+
+/**
+ * Ioctl API
+ *
+ * KDBUS_CMD_BUS_MAKE: After opening the "control" node, this command
+ * creates a new bus with the specified
+ * name. The bus is immediately shut down and
+ * cleaned up when the opened file descriptor is
+ * closed.
+ *
+ * KDBUS_CMD_ENDPOINT_MAKE: Creates a new named special endpoint to talk to
+ * the bus. Such endpoints usually carry a more
+ * restrictive policy and grant restricted access
+ * to specific applications.
+ * KDBUS_CMD_ENDPOINT_UPDATE: Update the properties of a custom enpoint. Used
+ * to update the policy.
+ *
+ * KDBUS_CMD_HELLO: By opening the bus node, a connection is
+ * created. After a HELLO the opened connection
+ * becomes an active peer on the bus.
+ * KDBUS_CMD_UPDATE: Update the properties of a connection. Used to
+ * update the metadata subscription mask and
+ * policy.
+ * KDBUS_CMD_BYEBYE: Disconnect a connection. If there are no
+ * messages queued up in the connection's pool,
+ * the call succeeds, and the handle is rendered
+ * unusable. Otherwise, -EBUSY is returned without
+ * any further side-effects.
+ * KDBUS_CMD_FREE: Release the allocated memory in the receiver's
+ * pool.
+ * KDBUS_CMD_CONN_INFO: Retrieve credentials and properties of the
+ * initial creator of the connection. The data was
+ * stored at registration time and does not
+ * necessarily represent the connected process or
+ * the actual state of the process.
+ * KDBUS_CMD_BUS_CREATOR_INFO: Retrieve information of the creator of the bus
+ * a connection is attached to.
+ *
+ * KDBUS_CMD_SEND: Send a message and pass data from userspace to
+ * the kernel.
+ * KDBUS_CMD_RECV: Receive a message from the kernel which is
+ * placed in the receiver's pool.
+ *
+ * KDBUS_CMD_NAME_ACQUIRE: Request a well-known bus name to associate with
+ * the connection. Well-known names are used to
+ * address a peer on the bus.
+ * KDBUS_CMD_NAME_RELEASE: Release a well-known name the connection
+ * currently owns.
+ * KDBUS_CMD_LIST: Retrieve the list of all currently registered
+ * well-known and unique names.
+ *
+ * KDBUS_CMD_MATCH_ADD: Install a match which broadcast messages should
+ * be delivered to the connection.
+ * KDBUS_CMD_MATCH_REMOVE: Remove a current match for broadcast messages.
+ */
+enum kdbus_ioctl_type {
+ /* bus owner (00-0f) */
+ KDBUS_CMD_BUS_MAKE = _IOW(KDBUS_IOCTL_MAGIC, 0x00,
+ struct kdbus_cmd),
+
+ /* endpoint owner (10-1f) */
+ KDBUS_CMD_ENDPOINT_MAKE = _IOW(KDBUS_IOCTL_MAGIC, 0x10,
+ struct kdbus_cmd),
+ KDBUS_CMD_ENDPOINT_UPDATE = _IOW(KDBUS_IOCTL_MAGIC, 0x11,
+ struct kdbus_cmd),
+
+ /* connection owner (80-ff) */
+ KDBUS_CMD_HELLO = _IOWR(KDBUS_IOCTL_MAGIC, 0x80,
+ struct kdbus_cmd_hello),
+ KDBUS_CMD_UPDATE = _IOW(KDBUS_IOCTL_MAGIC, 0x81,
+ struct kdbus_cmd),
+ KDBUS_CMD_BYEBYE = _IOW(KDBUS_IOCTL_MAGIC, 0x82,
+ struct kdbus_cmd),
+ KDBUS_CMD_FREE = _IOW(KDBUS_IOCTL_MAGIC, 0x83,
+ struct kdbus_cmd_free),
+ KDBUS_CMD_CONN_INFO = _IOR(KDBUS_IOCTL_MAGIC, 0x84,
+ struct kdbus_cmd_info),
+ KDBUS_CMD_BUS_CREATOR_INFO = _IOR(KDBUS_IOCTL_MAGIC, 0x85,
+ struct kdbus_cmd_info),
+ KDBUS_CMD_LIST = _IOR(KDBUS_IOCTL_MAGIC, 0x86,
+ struct kdbus_cmd_list),
+
+ KDBUS_CMD_SEND = _IOW(KDBUS_IOCTL_MAGIC, 0x90,
+ struct kdbus_cmd_send),
+ KDBUS_CMD_RECV = _IOR(KDBUS_IOCTL_MAGIC, 0x91,
+ struct kdbus_cmd_recv),
+
+ KDBUS_CMD_NAME_ACQUIRE = _IOW(KDBUS_IOCTL_MAGIC, 0xa0,
+ struct kdbus_cmd),
+ KDBUS_CMD_NAME_RELEASE = _IOW(KDBUS_IOCTL_MAGIC, 0xa1,
+ struct kdbus_cmd),
+
+ KDBUS_CMD_MATCH_ADD = _IOW(KDBUS_IOCTL_MAGIC, 0xb0,
+ struct kdbus_cmd_match),
+ KDBUS_CMD_MATCH_REMOVE = _IOW(KDBUS_IOCTL_MAGIC, 0xb1,
+ struct kdbus_cmd_match),
+};
+
+#endif /* _UAPI_KDBUS_H_ */
diff --git a/packaging/dbus-x11.spec b/packaging/dbus-x11.spec
index 8dcd7996..c10d1503 100644
--- a/packaging/dbus-x11.spec
+++ b/packaging/dbus-x11.spec
@@ -1,4 +1,5 @@
%bcond_with x
+%bcond_with kdbus
Name: dbus-x11
%define _name dbus
@@ -75,6 +76,9 @@ export V=1
%if %{with_systemd}
--enable-systemd \
%endif
+%if %{with kdbus}
+ --enable-kdbus-transport \
+%endif
--with-console-auth-dir=/var/run/dbus/at_console/ \
--with-systemdsystemunitdir=%{_unitdir} \
--enable-smack
diff --git a/packaging/dbus.spec b/packaging/dbus.spec
index cee5b479..0e550d19 100644
--- a/packaging/dbus.spec
+++ b/packaging/dbus.spec
@@ -1,5 +1,7 @@
%define dbus_user_uid 81
+%bcond_with kdbus
+
Name: dbus
Url: http://dbus.freedesktop.org/
Summary: D-Bus Message Bus System
@@ -90,6 +92,9 @@ export V=1
%if %{with_systemd}
--enable-systemd \
%endif
+%if %{with kdbus}
+ --enable-kdbus-transport \
+%endif
--with-console-auth-dir=/var/run/dbus/at_console/ \
--with-systemdsystemunitdir=%{_unitdir} \
--enable-smack \
diff --git a/packaging/libdbus.spec b/packaging/libdbus.spec
index 26604810..0494b8cc 100644
--- a/packaging/libdbus.spec
+++ b/packaging/libdbus.spec
@@ -1,3 +1,5 @@
+%bcond_with kdbus
+
Name: libdbus
Url: http://dbus.freedesktop.org/
Summary: Library package for D-Bus
@@ -68,6 +70,9 @@ export V=1
--enable-inotify \
--with-console-auth-dir=/var/run/dbus/at_console/ \
--with-systemdsystemunitdir=%{_unitdir} \
+%if %{with kdbus}
+ --enable-kdbus-transport \
+%endif
--enable-smack
make %{?_smp_mflags} -C dbus libdbus-1.la