mirror of
https://gitlab.freedesktop.org/dbus/dbus.git
synced 2026-02-16 03:50:27 +01:00
* glib/dbus-gthread.c: fix include * glib/dbus-glib.h: rename DBusMessageHandler for now. I think glib API needs to change, though, as you don't want to use DBusMessageFunction, you want to use the DBusMessageHandler object. Probably dbus_connection_open_with_g_main_loop() and dbus_connection_setup_g_main_loop() or something like that (but think of better names...) that just create a connection that has watch/timeout functions etc. already set up. * dbus/dbus-connection.c (dbus_connection_send_message_with_reply): new function just to show how the message handler helps us deal with replies. * dbus/dbus-list.c (_dbus_list_remove_last): new function * dbus/dbus-string.c (_dbus_string_test): free a string that wasn't * dbus/dbus-hash.c: use memory pools for the hash entries (rebuild_table): be more paranoid about overflow, and shrink table when we can (_dbus_hash_test): reduce number of sprintfs and write valid C89. Add tests for case where we grow and then shrink the hash table. * dbus/dbus-mempool.h, dbus/dbus-mempool.c: memory pools * dbus/dbus-connection.c (dbus_connection_register_handler) (dbus_connection_unregister_handler): new functions * dbus/dbus-message.c (dbus_message_get_name): new * dbus/dbus-list.c: fix docs typo * dbus/dbus-message-handler.h, dbus/dbus-message-handler.c: an object representing a handler for messages.
410 lines
11 KiB
C
410 lines
11 KiB
C
/* -*- mode: C; c-file-style: "gnu" -*- */
|
|
/* dbus-message.c DBusMessage object
|
|
*
|
|
* Copyright (C) 2002 Red Hat Inc.
|
|
*
|
|
* Licensed under the Academic Free License version 1.2
|
|
*
|
|
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*
|
|
*/
|
|
|
|
#include "dbus-internals.h"
|
|
#include "dbus-message.h"
|
|
#include "dbus-message-internal.h"
|
|
#include "dbus-memory.h"
|
|
#include "dbus-list.h"
|
|
#include <string.h>
|
|
|
|
/**
|
|
* @defgroup DBusMessageInternals DBusMessage implementation details
|
|
* @ingroup DBusInternals
|
|
* @brief DBusMessage private implementation details.
|
|
*
|
|
* The guts of DBusMessage and its methods.
|
|
*
|
|
* @{
|
|
*/
|
|
|
|
/**
|
|
* The largest-length message we allow
|
|
*
|
|
* @todo match this up with whatever the protocol spec says.
|
|
*/
|
|
#define _DBUS_MAX_MESSAGE_LENGTH (_DBUS_INT_MAX/16)
|
|
|
|
/**
|
|
* @brief Internals of DBusMessage
|
|
*
|
|
* Object representing a message received from or to be sent to
|
|
* another application. This is an opaque object, all members
|
|
* are private.
|
|
*/
|
|
struct DBusMessage
|
|
{
|
|
int refcount; /**< Reference count */
|
|
|
|
DBusString header; /**< Header network data, stored
|
|
* separately from body so we can
|
|
* independently realloc it.
|
|
*/
|
|
|
|
DBusString body; /**< Body network data. */
|
|
|
|
unsigned int locked : 1; /**< Message being sent, no modifications allowed. */
|
|
};
|
|
|
|
/**
|
|
* Gets the data to be sent over the network for this message.
|
|
* The header and then the body should be written out.
|
|
* This function is guaranteed to always return the same
|
|
* data once a message is locked (with _dbus_message_lock()).
|
|
*
|
|
* @param message the message.
|
|
* @param header return location for message header data.
|
|
* @param body return location for message body data.
|
|
*/
|
|
void
|
|
_dbus_message_get_network_data (DBusMessage *message,
|
|
const DBusString **header,
|
|
const DBusString **body)
|
|
{
|
|
_dbus_assert (message->locked);
|
|
|
|
*header = &message->header;
|
|
*body = &message->body;
|
|
}
|
|
|
|
/**
|
|
* Locks a message. Allows checking that applications don't keep a
|
|
* reference to a message in the outgoing queue and change it
|
|
* underneath us. Messages are locked when they enter the outgoing
|
|
* queue (dbus_connection_send_message()), and the library complains
|
|
* if the message is modified while locked.
|
|
*
|
|
* @param message the message to lock.
|
|
*/
|
|
void
|
|
_dbus_message_lock (DBusMessage *message)
|
|
{
|
|
message->locked = TRUE;
|
|
}
|
|
|
|
/** @} */
|
|
|
|
/**
|
|
* @defgroup DBusMessage DBusMessage
|
|
* @ingroup DBus
|
|
* @brief Message to be sent or received over a DBusConnection.
|
|
*
|
|
* A DBusMessage is the most basic unit of communication over a
|
|
* DBusConnection. A DBusConnection represents a stream of messages
|
|
* received from a remote application, and a stream of messages
|
|
* sent to a remote application.
|
|
*
|
|
* @{
|
|
*/
|
|
|
|
/**
|
|
* @typedef DBusMessage
|
|
*
|
|
* Opaque data type representing a message received from or to be
|
|
* sent to another application.
|
|
*/
|
|
|
|
/**
|
|
* Constructs a new message. Returns #NULL if memory
|
|
* can't be allocated for the message.
|
|
*
|
|
* @return a new DBusMessage, free with dbus_message_unref()
|
|
* @see dbus_message_unref()
|
|
*/
|
|
DBusMessage*
|
|
dbus_message_new (void)
|
|
{
|
|
DBusMessage *message;
|
|
|
|
message = dbus_new0 (DBusMessage, 1);
|
|
if (message == NULL)
|
|
return NULL;
|
|
|
|
message->refcount = 1;
|
|
|
|
if (!_dbus_string_init (&message->header, _DBUS_MAX_MESSAGE_LENGTH))
|
|
{
|
|
dbus_free (message);
|
|
return NULL;
|
|
}
|
|
|
|
if (!_dbus_string_init (&message->body, _DBUS_MAX_MESSAGE_LENGTH))
|
|
{
|
|
_dbus_string_free (&message->header);
|
|
dbus_free (message);
|
|
return NULL;
|
|
}
|
|
|
|
/* We need to decide what a message contains. ;-) */
|
|
/* (not bothering to check failure of these appends) */
|
|
_dbus_string_append (&message->header, "H");
|
|
_dbus_string_append_byte (&message->header, '\0');
|
|
_dbus_string_append (&message->body, "Body");
|
|
_dbus_string_append_byte (&message->body, '\0');
|
|
|
|
return message;
|
|
}
|
|
|
|
|
|
/**
|
|
* Increments the reference count of a DBusMessage.
|
|
*
|
|
* @param message The message
|
|
* @see dbus_message_unref
|
|
*/
|
|
void
|
|
dbus_message_ref (DBusMessage *message)
|
|
{
|
|
message->refcount += 1;
|
|
}
|
|
|
|
/**
|
|
* Decrements the reference count of a DBusMessage.
|
|
*
|
|
* @param message The message
|
|
* @see dbus_message_ref
|
|
*/
|
|
void
|
|
dbus_message_unref (DBusMessage *message)
|
|
{
|
|
_dbus_assert (message != NULL);
|
|
_dbus_assert (message->refcount > 0);
|
|
|
|
message->refcount -= 1;
|
|
if (message->refcount == 0)
|
|
{
|
|
_dbus_string_free (&message->header);
|
|
_dbus_string_free (&message->body);
|
|
|
|
dbus_free (message);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Gets the name of a message.
|
|
* @param message the message
|
|
* @returns the message name (should not be freed)
|
|
*/
|
|
const char*
|
|
dbus_message_get_name (DBusMessage *message)
|
|
{
|
|
/* FIXME */
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/** @} */
|
|
|
|
/**
|
|
* @addtogroup DBusMessageInternals
|
|
*
|
|
* @{
|
|
*/
|
|
/**
|
|
* @typedef DBusMessageLoader
|
|
*
|
|
* The DBusMessageLoader object encapsulates the process of converting
|
|
* a byte stream into a series of DBusMessage. It buffers the incoming
|
|
* bytes as efficiently as possible, and generates a queue of
|
|
* messages. DBusMessageLoader is typically used as part of a
|
|
* DBusTransport implementation. The DBusTransport then hands off
|
|
* the loaded messages to a DBusConnection, making the messages
|
|
* visible to the application.
|
|
*
|
|
*/
|
|
|
|
/**
|
|
* Implementation details of DBusMessageLoader.
|
|
* All members are private.
|
|
*/
|
|
struct DBusMessageLoader
|
|
{
|
|
int refcount; /**< Reference count. */
|
|
|
|
DBusString data; /**< Buffered data */
|
|
|
|
DBusList *messages; /**< Complete messages. */
|
|
|
|
unsigned int buffer_outstanding : 1; /**< Someone is using the buffer to read */
|
|
};
|
|
|
|
/**
|
|
* The initial buffer size of the message loader.
|
|
*
|
|
* @todo this should be based on min header size plus some average
|
|
* body size, or something. Or rather, the min header size only, if we
|
|
* want to try to read only the header, store that in a DBusMessage,
|
|
* then read only the body and store that, etc., depends on
|
|
* how we optimize _dbus_message_loader_get_buffer() and what
|
|
* the exact message format is.
|
|
*/
|
|
#define INITIAL_LOADER_DATA_LEN 32
|
|
|
|
/**
|
|
* Creates a new message loader. Returns #NULL if memory can't
|
|
* be allocated.
|
|
*
|
|
* @returns new loader, or #NULL.
|
|
*/
|
|
DBusMessageLoader*
|
|
_dbus_message_loader_new (void)
|
|
{
|
|
DBusMessageLoader *loader;
|
|
|
|
loader = dbus_new0 (DBusMessageLoader, 1);
|
|
if (loader == NULL)
|
|
return NULL;
|
|
|
|
loader->refcount = 1;
|
|
|
|
if (!_dbus_string_init (&loader->data, _DBUS_INT_MAX))
|
|
{
|
|
dbus_free (loader);
|
|
return NULL;
|
|
}
|
|
|
|
/* preallocate the buffer for speed, ignore failure */
|
|
(void) _dbus_string_set_length (&loader->data, INITIAL_LOADER_DATA_LEN);
|
|
|
|
return loader;
|
|
}
|
|
|
|
/**
|
|
* Increments the reference count of the loader.
|
|
*
|
|
* @param loader the loader.
|
|
*/
|
|
void
|
|
_dbus_message_loader_ref (DBusMessageLoader *loader)
|
|
{
|
|
loader->refcount += 1;
|
|
}
|
|
|
|
/**
|
|
* Decrements the reference count of the loader and finalizes the
|
|
* loader when the count reaches zero.
|
|
*
|
|
* @param loader the loader.
|
|
*/
|
|
void
|
|
_dbus_message_loader_unref (DBusMessageLoader *loader)
|
|
{
|
|
loader->refcount -= 1;
|
|
if (loader->refcount == 0)
|
|
{
|
|
_dbus_list_foreach (&loader->messages,
|
|
(DBusForeachFunction) dbus_message_unref,
|
|
NULL);
|
|
_dbus_list_clear (&loader->messages);
|
|
_dbus_string_free (&loader->data);
|
|
dbus_free (loader);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Gets the buffer to use for reading data from the network. Network
|
|
* data is read directly into an allocated buffer, which is then used
|
|
* in the DBusMessage, to avoid as many extra memcpy's as possible.
|
|
* The buffer must always be returned immediately using
|
|
* _dbus_message_loader_return_buffer(), even if no bytes are
|
|
* successfully read.
|
|
*
|
|
* @todo this function can be a lot more clever. For example
|
|
* it can probably always return a buffer size to read exactly
|
|
* the body of the next message, thus avoiding any memory wastage
|
|
* or reallocs.
|
|
*
|
|
* @param loader the message loader.
|
|
* @param buffer the buffer
|
|
*/
|
|
void
|
|
_dbus_message_loader_get_buffer (DBusMessageLoader *loader,
|
|
DBusString **buffer)
|
|
{
|
|
_dbus_assert (!loader->buffer_outstanding);
|
|
|
|
*buffer = &loader->data;
|
|
|
|
loader->buffer_outstanding = TRUE;
|
|
}
|
|
|
|
/**
|
|
* Returns a buffer obtained from _dbus_message_loader_get_buffer(),
|
|
* indicating to the loader how many bytes of the buffer were filled
|
|
* in. This function must always be called, even if no bytes were
|
|
* successfully read.
|
|
*
|
|
* @param loader the loader.
|
|
* @param buffer the buffer.
|
|
* @param bytes_read number of bytes that were read into the buffer.
|
|
*/
|
|
void
|
|
_dbus_message_loader_return_buffer (DBusMessageLoader *loader,
|
|
DBusString *buffer,
|
|
int bytes_read)
|
|
{
|
|
_dbus_assert (loader->buffer_outstanding);
|
|
_dbus_assert (buffer == &loader->data);
|
|
|
|
/* FIXME fake implementation just creates a message for every 7
|
|
* bytes. The real implementation will pass ownership of
|
|
* loader->data bytes to new messages, to avoid memcpy. We can also
|
|
* smart-realloc loader->data to shrink it if it's too big, though
|
|
* _dbus_message_loader_get_buffer() could strategically arrange for
|
|
* that to usually not happen.
|
|
*/
|
|
|
|
loader->buffer_outstanding = FALSE;
|
|
|
|
while (_dbus_string_get_length (&loader->data) >= 7)
|
|
{
|
|
DBusMessage *message;
|
|
|
|
message = dbus_message_new ();
|
|
if (message == NULL)
|
|
break; /* ugh, postpone this I guess. */
|
|
|
|
_dbus_list_append (&loader->messages, message);
|
|
|
|
_dbus_string_delete (&loader->data,
|
|
0, 7);
|
|
|
|
_dbus_verbose ("Loaded message %p\n", message);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Pops a loaded message (passing ownership of the message
|
|
* to the caller). Returns #NULL if no messages have been
|
|
* loaded.
|
|
*
|
|
* @param loader the loader.
|
|
* @returns the next message, or #NULL if none.
|
|
*/
|
|
DBusMessage*
|
|
_dbus_message_loader_pop_message (DBusMessageLoader *loader)
|
|
{
|
|
return _dbus_list_pop_first (&loader->messages);
|
|
}
|
|
|
|
/** @} */
|