diff options
author | Adrian Szyndela <adrian.s@samsung.com> | 2016-01-07 14:48:48 +0100 |
---|---|---|
committer | Adrian Szyndela <adrian.s@samsung.com> | 2016-01-13 15:11:46 +0100 |
commit | 59067c7f66bfdb199da221fde56ad59b09a3fd27 (patch) | |
tree | fdb10cec44955d3c9c360f632ba5a4e34b89844b | |
parent | ebc0fa80b2903379a9e179915fbb10fc7ecd7575 (diff) | |
download | dbus-59067c7f66bfdb199da221fde56ad59b09a3fd27.tar.gz dbus-59067c7f66bfdb199da221fde56ad59b09a3fd27.tar.bz2 dbus-59067c7f66bfdb199da221fde56ad59b09a3fd27.zip |
Add kdbus transportsubmit/tizen/20160114.014200accepted/tizen/wearable/20160114.064410accepted/tizen/tv/20160114.064349accepted/tizen/mobile/20160114.064325
This reverts commit 197db0df78e0a9f82df4162885854f99ff749505,
which reverts commit 771f5155c9a393a242329988d56661a51fc04e6c.
Change-Id: I4133020c585a6f9b42f487ffae4d2005b25a1d08
35 files changed, 11206 insertions, 461 deletions
@@ -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, ¤t_alignment, 2); + break; + case DBUS_TYPE_INT32: + case DBUS_TYPE_UINT32: + case DBUS_TYPE_UNIX_FD: + res = update_size (res, 4, ¤t_alignment, 4); + break; + case DBUS_TYPE_INT64: + case DBUS_TYPE_UINT64: + case DBUS_TYPE_DOUBLE: + res = update_size (res, 8, ¤t_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, ¤t_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, ¤t_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, ®istration_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 |