diff options
| author | Havoc Pennington <hp@redhat.com> | 2002-11-25 05:13:09 +0000 | 
|---|---|---|
| committer | Havoc Pennington <hp@redhat.com> | 2002-11-25 05:13:09 +0000 | 
| commit | 041b0767b284034aee09e9a0de2a3844b8cc546a (patch) | |
| tree | 8ef9cce16d743350971696ff6333ce43686c7ac0 | |
| parent | 576cdb6e0b1274e9fa5276e01337aef330dd4e8c (diff) | |
2002-11-24  Havoc Pennington  <hp@pobox.com>
        * test/echo-client.c, test/echo-server.c: cheesy test
	clients.
	* configure.in (AC_CHECK_FUNCS): check for writev
	* dbus/dbus-message.c (_dbus_message_get_network_data): new
	function
	* dbus/dbus-list.c (_dbus_list_foreach): new function
	* dbus/dbus-internals.c (_dbus_verbose): new function
	* dbus/dbus-server.c, dbus/dbus-server.h: public object
	representing a server that listens for connections.
	* dbus/.cvsignore: create
	* dbus/dbus-errors.h, dbus/dbus-errors.c:
	public API for reporting errors
	* dbus/dbus-connection.h, dbus/dbus-connection.c:
	public object representing a connection that
	sends/receives messages. (Same object used for
	both client and server.)
	* dbus/dbus-transport.h, dbus/dbus-transport.c:
	Basic abstraction for different kinds of stream
	that we might read/write messages from.
39 files changed, 4389 insertions, 15 deletions
diff --git a/.cvsignore b/.cvsignore new file mode 100644 index 00000000..7487721d --- /dev/null +++ b/.cvsignore @@ -0,0 +1,16 @@ +config.log +config.status +config.sub +configure +*.pc +libtool +ltmain.sh +stamp-h1 +Doxyfile +Makefile +Makefile.in +aclocal.m4 +autom4te.cache +config.guess +config.h +config.h.in @@ -1,3 +1,34 @@ +2002-11-24  Havoc Pennington  <hp@pobox.com> + +        * test/echo-client.c, test/echo-server.c: cheesy test  +	clients. +	 +	* configure.in (AC_CHECK_FUNCS): check for writev + +	* dbus/dbus-message.c (_dbus_message_get_network_data): new +	function + +	* dbus/dbus-list.c (_dbus_list_foreach): new function + +	* dbus/dbus-internals.c (_dbus_verbose): new function + +	* dbus/dbus-server.c, dbus/dbus-server.h: public object +	representing a server that listens for connections. + +	* dbus/.cvsignore: create + +	* dbus/dbus-errors.h, dbus/dbus-errors.c: +	public API for reporting errors + +	* dbus/dbus-connection.h, dbus/dbus-connection.c: +	public object representing a connection that  +	sends/receives messages. (Same object used for  +	both client and server.) + +	* dbus/dbus-transport.h, dbus/dbus-transport.c: +	Basic abstraction for different kinds of stream +	that we might read/write messages from. +  2002-11-23  Havoc Pennington  <hp@pobox.com>  	* dbus/dbus-internals.h (_DBUS_INT_MAX): add _DBUS_INT_MIN  diff --git a/bus/.cvsignore b/bus/.cvsignore new file mode 100644 index 00000000..8b966130 --- /dev/null +++ b/bus/.cvsignore @@ -0,0 +1,7 @@ +.deps +.libs +Makefile +Makefile.in +*.lo +*.la +dbus-daemon-1 diff --git a/configure.in b/configure.in index d8ba3c6a..4440040d 100644 --- a/configure.in +++ b/configure.in @@ -102,6 +102,10 @@ AC_C_BIGENDIAN  AC_CHECK_FUNCS(vsnprintf vasprintf) +dnl check for writev header and writev function so we're  +dnl good to go if HAVE_WRITEV gets defined. +AC_CHECK_HEADERS(sys/uio.h, [AC_CHECK_FUNCS(writev)]) +  DBUS_CLIENT_CFLAGS=  DBUS_CLIENT_LIBS=  AC_SUBST(DBUS_CLIENT_CFLAGS) @@ -112,6 +116,11 @@ DBUS_BUS_LIBS=  AC_SUBST(DBUS_BUS_CFLAGS)  AC_SUBST(DBUS_BUS_LIBS) +DBUS_TEST_CFLAGS= +DBUS_TEST_LIBS= +AC_SUBST(DBUS_TEST_CFLAGS) +AC_SUBST(DBUS_TEST_LIBS) +  AC_OUTPUT([  Makefile  Doxyfile diff --git a/dbus/.cvsignore b/dbus/.cvsignore new file mode 100644 index 00000000..4ebd42b8 --- /dev/null +++ b/dbus/.cvsignore @@ -0,0 +1,7 @@ +.deps +.libs +Makefile +Makefile.in +*.lo +*.la +dbus-test diff --git a/dbus/Makefile.am b/dbus/Makefile.am index 46d703f0..f830bb7b 100644 --- a/dbus/Makefile.am +++ b/dbus/Makefile.am @@ -7,18 +7,38 @@ lib_LTLIBRARIES=libdbus-1.la  dbusinclude_HEADERS=				\  	dbus.h					\ +	dbus-errors.h				\  	dbus-macros.h				\  	dbus-memory.h				\  	dbus-message.h				\ +	dbus-server.h				\  	dbus-types.h  libdbus_1_la_SOURCES=				\ +	dbus-connection.c			\ +	dbus-connection-internal.h		\ +	dbus-errors.c				\  	dbus-memory.c				\ -	dbus-message.c +	dbus-message.c				\ +	dbus-message-internal.h			\ +	dbus-server.c				\ +	dbus-server-protected.h			\ +	dbus-server-unix.c			\ +	dbus-server-unix.h			\ +	dbus-transport.c			\ +	dbus-transport.h			\ +	dbus-transport-protected.h		\ +	dbus-transport-unix.c			\ +	dbus-transport-unix.h			\ +	dbus-watch.c				\ +	dbus-watch.h +  ## this library is linked into both libdbus and the bus   ## itself, but does not export any symbols from libdbus. -## It contains utility functions not in the public API. +## i.e. the point is to contain symbols that we don't  +## want the shared lib to export, but we do want the  +## message bus to be able to use.  noinst_LTLIBRARIES=libdbus-convenience.la diff --git a/dbus/dbus-connection-internal.h b/dbus/dbus-connection-internal.h new file mode 100644 index 00000000..7260bde0 --- /dev/null +++ b/dbus/dbus-connection-internal.h @@ -0,0 +1,64 @@ +/* -*- mode: C; c-file-style: "gnu" -*- */ +/* dbus-connection-internal.h DBusConnection internal interfaces + * + * 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 + * + */ +#ifndef DBUS_CONNECTION_INTERNAL_H +#define DBUS_CONNECTION_INTERNAL_H + +#include <dbus/dbus-internals.h> +#include <dbus/dbus-connection.h> +#include <dbus/dbus-message.h> +#include <dbus/dbus-transport.h> + +DBUS_BEGIN_DECLS; + +typedef enum +{ +  DBUS_ITERATION_DO_WRITING = 1 << 0, /**< Write messages out. */ +  DBUS_ITERATION_DO_READING = 1 << 1, /**< Read messages in. */ +  DBUS_ITERATION_BLOCK      = 1 << 2  /**< Block if nothing to do. */ +} DBusIterationFlags; + +dbus_bool_t     _dbus_connection_queue_received_message (DBusConnection *connection, +                                                         DBusMessage    *message); +dbus_bool_t     _dbus_connection_have_messages_to_send  (DBusConnection *connection); + +DBusMessage*    _dbus_connection_get_message_to_send    (DBusConnection *connection); +void            _dbus_connection_message_sent           (DBusConnection *connection, +                                                         DBusMessage    *message); + +dbus_bool_t     _dbus_connection_add_watch              (DBusConnection *connection, +                                                         DBusWatch      *watch); +void            _dbus_connection_remove_watch           (DBusConnection *connection, +                                                         DBusWatch      *watch); +DBusConnection* _dbus_connection_new_for_transport      (DBusTransport  *transport); + + +void            _dbus_connection_do_iteration           (DBusConnection *connection, +                                                         unsigned int    flags, +                                                         int             timeout_milliseconds); + +void            _dbus_connection_transport_error        (DBusConnection *connection, +                                                         DBusResultCode  result_code); + +DBUS_END_DECLS; + +#endif /* DBUS_CONNECTION_INTERNAL_H */ diff --git a/dbus/dbus-connection.c b/dbus/dbus-connection.c new file mode 100644 index 00000000..f60e53a9 --- /dev/null +++ b/dbus/dbus-connection.c @@ -0,0 +1,623 @@ +/* -*- mode: C; c-file-style: "gnu" -*- */ +/* dbus-connection.c DBusConnection 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-connection.h" +#include "dbus-list.h" +#include "dbus-transport.h" +#include "dbus-watch.h" +#include "dbus-connection-internal.h" +#include "dbus-list.h" + +/** + * @defgroup DBusConnection DBusConnection + * @ingroup  DBus + * @brief Connection to another application + * + * A DBusConnection represents a connection to another + * application. Messages can be sent and received via this connection. + * + * The connection maintains a queue of incoming messages and a queue + * of outgoing messages. dbus_connection_pop_message() and friends + * can be used to read incoming messages from the queue. + * Outgoing messages are automatically discarded as they are + * written to the network. + * + * In brief a DBusConnection is a message queue associated with some + * message transport mechanism such as a socket. + *  + */ + +/** + * @defgroup DBusConnectionInternals DBusConnection implementation details + * @ingroup  DBusInternals + * @brief Implementation details of DBusConnection + * + * @{ + */ + +/** + * Implementation details of DBusConnection. All fields are private. + */ +struct DBusConnection +{ +  int refcount; /**< Reference count. */ + +  DBusList *outgoing_messages; /**< Queue of messages we need to send, send the end of the list first. */ +  DBusList *incoming_messages; /**< Queue of messages we have received, end of the list received most recently. */ + +  int n_outgoing;              /**< Length of outgoing queue. */ +  int n_incoming;              /**< Length of incoming queue. */ +   +  DBusTransport *transport;    /**< Object that sends/receives messages over network. */ +  DBusWatchList *watches;      /**< Stores active watches. */ +   +  DBusConnectionErrorFunction error_function; /**< Callback for errors. */ +  void *error_data;                           /**< Data for error callback. */ +  DBusFreeFunction error_free_data_function;  /**< Free function for error callback data. */ +}; + +/** + * Adds a message to the incoming message queue, returning #FALSE + * if there's insufficient memory to queue the message. + * + * @param connection the connection. + * @param message the message to queue. + * @returns #TRUE on success. + */ +dbus_bool_t +_dbus_connection_queue_received_message (DBusConnection *connection, +                                         DBusMessage    *message) +{ +  if (!_dbus_list_append (&connection->incoming_messages, +                          message)) +    return FALSE; + +  dbus_message_ref (message); +  connection->n_incoming += 1; + +  return TRUE; +} + +/** + * Checks whether there are messages in the outgoing message queue. + * + * @param connection the connection. + * @returns #TRUE if the outgoing queue is non-empty. + */ +dbus_bool_t +_dbus_connection_have_messages_to_send (DBusConnection *connection) +{ +  return connection->outgoing_messages != NULL; +} + +/** + * Gets the next outgoing message. The message remanins in the + * queue, and the caller does not own a reference to it. + * + * @param connection the connection. + * @returns the message to be sent. + */  +DBusMessage* +_dbus_connection_get_message_to_send (DBusConnection *connection) +{ +  return _dbus_list_get_last (&connection->outgoing_messages); +} + +/** + * Notifies the connection that a message has been sent, so the + * message can be removed from the outgoing queue. + * + * @param connection the connection. + * @param message the message that was sent. + */ +void +_dbus_connection_message_sent (DBusConnection *connection, +                               DBusMessage    *message) +{ +  _dbus_assert (message == _dbus_list_get_last (&connection->outgoing_messages)); +  _dbus_list_pop_last (&connection->outgoing_messages); +  dbus_message_unref (message); +   +  connection->n_outgoing -= 1; +  if (connection->n_outgoing == 0) +    _dbus_transport_messages_pending (connection->transport, +                                      connection->n_outgoing);   +} + +/** + * Adds a watch using the connection's DBusAddWatchFunction if + * available. Otherwise records the watch to be added when said + * function is available. Also re-adds the watch if the + * DBusAddWatchFunction changes. May fail due to lack of memory. + * + * @param connection the connection. + * @param watch the watch to add. + * @returns #TRUE on success. + */ +dbus_bool_t +_dbus_connection_add_watch (DBusConnection *connection, +                            DBusWatch      *watch) +{ +  return _dbus_watch_list_add_watch (connection->watches, +                                     watch); +   +  return TRUE; +} + +/** + * Removes a watch using the connection's DBusRemoveWatchFunction + * if available. It's an error to call this function on a watch + * that was not previously added. + * + * @param connection the connection. + * @param watch the watch to remove. + */ +void +_dbus_connection_remove_watch (DBusConnection *connection, +                               DBusWatch      *watch) +{ +  _dbus_watch_list_remove_watch (connection->watches, +                                 watch); +} + +static void +handle_error (DBusConnection *connection, +              DBusResultCode  result) +{ +  if (result != DBUS_RESULT_SUCCESS && +      connection->error_function != NULL) +    { +      dbus_connection_ref (connection); +      (* connection->error_function) (connection, result, +                                      connection->error_data); +      dbus_connection_unref (connection); +    } +} + +static void +set_result_handled (DBusConnection *connection, +                    DBusResultCode *result_address, +                    DBusResultCode  result) +{ +  dbus_set_result (result_address, result); +  handle_error (connection, result); +} + +/** + * Reports a transport error to the connection. Typically + * results in an application error callback being invoked. + * + * @param connection the connection. + * @param result_code the error code. + */ +void +_dbus_connection_transport_error (DBusConnection *connection, +                                  DBusResultCode  result_code) +{ +  handle_error (connection, result_code); +} + +/** + * Queues incoming messages and sends outgoing messages for this + * connection, optionally blocking in the process. Each call to + * _dbus_connection_do_iteration() will call select() or poll() one + * time and then read or write data if possible. + * + * The purpose of this function is to be able to flush outgoing + * messages or queue up incoming messages without returning + * control to the application and causing reentrancy weirdness. + * + * The flags parameter allows you to specify whether to + * read incoming messages, write outgoing messages, or both, + * and whether to block if no immediate action is possible. + * + * The timeout_milliseconds parameter does nothing unless the + * iteration is blocking. + * + * If there are no outgoing messages and DBUS_ITERATION_DO_READING + * wasn't specified, then it's impossible to block, even if + * you specify DBUS_ITERATION_BLOCK; in that case the function + * returns immediately. + *  + * @param connection the connection. + * @param flags iteration flags. + * @param timeout_milliseconds maximum blocking time, or -1 for no limit. + */ +void +_dbus_connection_do_iteration (DBusConnection *connection, +                               unsigned int    flags, +                               int             timeout_milliseconds) +{ +  if (connection->n_outgoing == 0) +    flags &= ~DBUS_ITERATION_DO_WRITING; +   +  _dbus_transport_do_iteration (connection->transport, +                                flags, timeout_milliseconds); +} + +/** + * Creates a new connection for the given transport.  A transport + * represents a message stream that uses some concrete mechanism, such + * as UNIX domain sockets. May return #NULL if insufficient + * memory exists to create the connection. + * + * @param transport the transport. + * @returns the new connection, or #NULL on failure. + */ +DBusConnection* +_dbus_connection_new_for_transport (DBusTransport *transport) +{ +  DBusConnection *connection; +  DBusWatchList *watch_list; +   +  watch_list = NULL; +  connection = NULL; +   +  watch_list = _dbus_watch_list_new (); +  if (watch_list == NULL) +    goto error; +   +  connection = dbus_new0 (DBusConnection, 1); +  if (connection == NULL) +    goto error; +   +  connection->refcount = 1; +  connection->transport = transport; +  connection->watches = watch_list; + +  _dbus_transport_ref (transport); +  _dbus_transport_set_connection (transport, connection); +   +  return connection; +   + error: + +  _dbus_assert (connection == NULL);   + +  if (watch_list) +    _dbus_watch_list_free (watch_list); +   +  return NULL; +} + +/** @} */ + +/** + * @addtogroup DBusConnection + * + * @{ + */ + +/** + * Opens a new connection to a remote address. + * + * @todo specify what the address parameter is. Right now + * it's just the name of a UNIX domain socket. It should be + * something more complex that encodes which transport to use. + * + * If the open fails, the function returns #NULL, and provides + * a reason for the failure in the result parameter. Pass + * #NULL for the result parameter if you aren't interested + * in the reason for failure. + *  + * @param address the address. + * @param result address where a result code can be returned. + * @returns new connection, or #NULL on failure. + */ +DBusConnection* +dbus_connection_open (const char     *address, +                      DBusResultCode *result) +{ +  DBusConnection *connection; +  DBusTransport *transport; +   +  transport = _dbus_transport_open (address, result); +  if (transport == NULL) +    return NULL; +   +  connection = _dbus_connection_new_for_transport (transport); + +  _dbus_transport_unref (transport); +   +  if (connection == NULL) +    { +      dbus_set_result (result, DBUS_RESULT_NO_MEMORY); +      return NULL; +    } +   +  return connection; +} + +/** + * Increments the reference count of a DBusConnection. + * + * @param connection the connection. + */ +void +dbus_connection_ref (DBusConnection *connection) +{ +  connection->refcount += 1; +} + +/** + * Decrements the reference count of a DBusConnection, and finalizes + * it if the count reaches zero.  If a connection is still connected + * when it's finalized, it will be disconnected (that is, associated + * file handles will be closed). + * + * @param connection the connection. + */ +void +dbus_connection_unref (DBusConnection *connection) +{ +  _dbus_assert (connection != NULL); +  _dbus_assert (connection->refcount > 0); +   +  connection->refcount -= 1; +  if (connection->refcount == 0) +    { +      /* free error data as a side effect */ +      dbus_connection_set_error_function (connection, +                                          NULL, NULL, NULL); + +      _dbus_watch_list_free (connection->watches); +       +      _dbus_list_foreach (&connection->outgoing_messages, +                          (DBusForeachFunction) dbus_message_unref, +                          NULL); +      _dbus_list_clear (&connection->outgoing_messages); + +      _dbus_list_foreach (&connection->incoming_messages, +                          (DBusForeachFunction) dbus_message_unref, +                          NULL); +      _dbus_list_clear (&connection->incoming_messages); +       +      _dbus_transport_unref (connection->transport); +       +      dbus_free (connection); +    } +} + +/** + * Closes the connection, so no further data can be sent or received. + * Any further attempts to send data will result in errors.  This + * function does not affect the connection's reference count.  It's + * safe to disconnect a connection more than once; all calls after the + * first do nothing. It's impossible to "reconnect" a connection, a + * new connection must be created. + * + * @param connection the connection. + */ +void +dbus_connection_disconnect (DBusConnection *connection) +{ +  _dbus_transport_disconnect (connection->transport); +} + +/** + * Gets whether the connection is currently connected.  All + * connections are connected when they are opened.  A connection may + * become disconnected when the remote application closes its end, or + * exits; a connection may also be disconnected with + * dbus_connection_disconnect(). + * + * @param connection the connection. + * @returns #TRUE if the connection is still alive. + */ +dbus_bool_t +dbus_connection_get_is_connected (DBusConnection *connection) +{ +  return _dbus_transport_get_is_connected (connection->transport); +} + +/** + * Adds a message to the outgoing message queue. Does not block to + * write the message to the network; that happens asynchronously. to + * force the message to be written, call dbus_connection_flush(). + * + * If the function fails, it returns #FALSE and returns the + * reason for failure via the result parameter. + * The result parameter can be #NULL if you aren't interested + * in the reason for the failure. + *  + * @param connection the connection. + * @param message the message to write. + * @param result address where result code can be placed. + * @returns #TRUE on success. + */ +dbus_bool_t +dbus_connection_send_message (DBusConnection *connection, +                              DBusMessage    *message, +                              DBusResultCode *result) +{   +  if (!_dbus_list_prepend (&connection->outgoing_messages, +                           message)) +    { +      set_result_handled (connection, result, DBUS_RESULT_NO_MEMORY); +      return FALSE; +    } + +  dbus_message_ref (message); +  connection->n_outgoing += 1; + +  _dbus_message_lock (message); +   +  if (connection->n_outgoing == 1) +    _dbus_transport_messages_pending (connection->transport, +                                      connection->n_outgoing); +} + +/** + * Blocks until the outgoing message queue is empty. + * + * @param connection the connection. + */ +void +dbus_connection_flush (DBusConnection *connection) +{ +  while (connection->n_outgoing > 0) +    _dbus_connection_do_iteration (connection, +                                   DBUS_ITERATION_DO_WRITING | +                                   DBUS_ITERATION_BLOCK, +                                   -1); +} + +/** + * Gets the number of messages in the incoming message queue. + * + * @param connection the connection. + * @returns the number of messages in the queue. + */ +int +dbus_connection_get_n_messages (DBusConnection *connection) +{ +  return connection->n_incoming; +} + +/** + * Returns the first-received message from the incoming message queue, + * leaving it in the queue. The caller does not own a reference to the + * returned message. If the queue is empty, returns #NULL. + * + * @param connection the connection. + * @returns next message in the incoming queue. + */ +DBusMessage* +dbus_connection_peek_message  (DBusConnection *connection) +{ +  return _dbus_list_get_first (&connection->incoming_messages); +} + +/** + * Returns the first-received message from the incoming message queue, + * removing it from the queue. The caller owns a reference to the + * returned message. If the queue is empty, returns #NULL. + * + * @param connection the connection. + * @returns next message in the incoming queue. + */ +DBusMessage* +dbus_connection_pop_message (DBusConnection *connection) +{ +  if (connection->n_incoming > 0) +    { +      connection->n_incoming -= 1; +      return _dbus_list_pop_first (&connection->incoming_messages); +    } +  else +    return NULL; +} + +/** + * Sets the error handler function for the connection. + *  + * @param connection the connection. + * @param error_function the error handler. + * @param data data to pass to the error handler. + * @param free_data_function function to be called to free the data. + */ +void +dbus_connection_set_error_function  (DBusConnection              *connection, +                                     DBusConnectionErrorFunction  error_function, +                                     void                        *data, +                                     DBusFreeFunction             free_data_function) +{ +  if (connection->error_free_data_function != NULL) +    (* connection->error_free_data_function) (connection->error_data); + +  connection->error_function = error_function; +  connection->error_data = data; +  connection->error_free_data_function = free_data_function; +} + +/** + * Sets the watch functions for the connection. These functions are + * responsible for making the application's main loop aware of file + * descriptors that need to be monitored for events, using select() or + * poll(). When using Qt, typically the DBusAddWatchFunction would + * create a QSocketNotifier. When using GLib, the DBusAddWatchFunction + * could call g_io_add_watch(), or could be used as part of a more + * elaborate GSource. + * + * The DBusWatch can be queried for the file descriptor to watch using + * dbus_watch_get_fd(), and for the events to watch for using + * dbus_watch_get_flags(). The flags returned by + * dbus_watch_get_flags() will only contain DBUS_WATCH_READABLE and + * DBUS_WATCH_WRITABLE, never DBUS_WATCH_HANGUP or DBUS_WATCH_ERROR; + * all watches implicitly include a watch for hangups, errors, and + * other exceptional conditions. + * + * Once a file descriptor becomes readable or writable, or an exception + * occurs, dbus_connection_handle_watch() should be called to + * notify the connection of the file descriptor's condition. + * + * dbus_connection_handle_watch() cannot be called during the + * DBusAddWatchFunction, as the connection will not be ready to handle + * that watch yet. + *  + * It is not allowed to reference a DBusWatch after it has been passed + * to remove_function. + *  + * @param connection the connection. + * @param add_function function to begin monitoring a new descriptor. + * @param remove_function function to stop monitoring a descriptor. + * @param data data to pass to add_function and remove_function. + * @param free_data_function function to be called to free the data. + */ +void +dbus_connection_set_watch_functions (DBusConnection              *connection, +                                     DBusAddWatchFunction         add_function, +                                     DBusRemoveWatchFunction      remove_function, +                                     void                        *data, +                                     DBusFreeFunction             free_data_function) +{ +  /* ref connection for slightly better reentrancy */ +  dbus_connection_ref (connection); +   +  _dbus_watch_list_set_functions (connection->watches, +                                  add_function, remove_function, +                                  data, free_data_function); +   +  /* drop our paranoid refcount */ +  dbus_connection_unref (connection); +} + +/** + * Called to notify the connection when a previously-added watch + * is ready for reading or writing, or has an exception such + * as a hangup. + * + * @param connection the connection. + * @param watch the watch. + * @param condition the current condition of the file descriptors being watched. + */ +void +dbus_connection_handle_watch (DBusConnection              *connection, +                              DBusWatch                   *watch, +                              unsigned int                 condition) +{ +  _dbus_transport_handle_watch (connection->transport, +                                watch, condition); +} + +/** @} */ diff --git a/dbus/dbus-connection.h b/dbus/dbus-connection.h new file mode 100644 index 00000000..ceb5728f --- /dev/null +++ b/dbus/dbus-connection.h @@ -0,0 +1,100 @@ +/* -*- mode: C; c-file-style: "gnu" -*- */ +/* dbus-connection.h DBusConnection 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 + * + */ +#if !defined (DBUS_INSIDE_DBUS_H) && !defined (DBUS_COMPILATION) +#error "Only <dbus/dbus.h> can be included directly, this file may disappear or change contents." +#endif + +#ifndef DBUS_CONNECTION_H +#define DBUS_CONNECTION_H + +#include <dbus/dbus-errors.h> +#include <dbus/dbus-message.h> +#include <dbus/dbus-memory.h> + +DBUS_BEGIN_DECLS; + +typedef struct DBusConnection DBusConnection; +typedef struct DBusWatch DBusWatch; + +typedef enum +{ +  DBUS_WATCH_READABLE = 1 << 0, /**< As in POLLIN */ +  DBUS_WATCH_WRITABLE = 1 << 1, /**< As in POLLOUT */ +  DBUS_WATCH_ERROR    = 1 << 2, /**< As in POLLERR (can't watch for this, but +                                 *   the flag can be passed to dbus_connection_handle_watch()). +                                 */ +  DBUS_WATCH_HANGUP   = 1 << 3  /**< As in POLLHUP (can't watch for it, but +                                 *   can be present in current state). */ +} DBusWatchFlags; + +typedef void (* DBusAddWatchFunction)    (DBusWatch      *watch, +                                          void           *data); + +typedef void (* DBusRemoveWatchFunction) (DBusWatch      *watch, +                                          void           *data); + +typedef void (* DBusConnectionErrorFunction) (DBusConnection *connection, +                                              DBusResultCode  error_code, +                                              void           *data); + +DBusConnection* dbus_connection_open             (const char     *address, +                                                  DBusResultCode *result); +void            dbus_connection_ref              (DBusConnection *connection); +void            dbus_connection_unref            (DBusConnection *connection); +void            dbus_connection_disconnect       (DBusConnection *connection); +dbus_bool_t     dbus_connection_get_is_connected (DBusConnection *connection); +dbus_bool_t     dbus_connection_send_message     (DBusConnection *connection, +                                                  DBusMessage    *message, +                                                  DBusResultCode *result); +void            dbus_connection_flush            (DBusConnection *connection); + +int          dbus_connection_get_n_messages      (DBusConnection *connection); +DBusMessage* dbus_connection_peek_message        (DBusConnection *connection); +DBusMessage* dbus_connection_pop_message         (DBusConnection *connection); + + +void dbus_connection_set_error_function  (DBusConnection              *connection, +                                          DBusConnectionErrorFunction  error_function, +                                          void                        *data, +                                          DBusFreeFunction             free_data_function); +void dbus_connection_set_watch_functions (DBusConnection              *connection, +                                          DBusAddWatchFunction         add_function, +                                          DBusRemoveWatchFunction      remove_function, +                                          void                        *data, +                                          DBusFreeFunction             free_data_function); +void dbus_connection_handle_watch        (DBusConnection              *connection, +                                          DBusWatch                   *watch, +                                          unsigned int                 condition); + + +int          dbus_watch_get_fd    (DBusWatch        *watch); +unsigned int dbus_watch_get_flags (DBusWatch        *watch); +void*        dbus_watch_get_data  (DBusWatch        *watch); +void         dbus_watch_set_data  (DBusWatch        *watch, +                                   void             *data, +                                   DBusFreeFunction  free_data_function); + + +DBUS_END_DECLS; + +#endif /* DBUS_CONNECTION_H */ diff --git a/dbus/dbus-errors.c b/dbus/dbus-errors.c new file mode 100644 index 00000000..1214c1f5 --- /dev/null +++ b/dbus/dbus-errors.c @@ -0,0 +1,109 @@ +/* -*- mode: C; c-file-style: "gnu" -*- */ +/* dbus-errors.c Error reporting + * + * 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-errors.h" + +/** + * @defgroup DBusErrors Error reporting + * @ingroup  DBus + * @brief Error reporting + * + * Types and functions related to reporting errors. + * + * + * In essence D-BUS error reporting works as follows: + * + * @code + * DBusResultCode result = DBUS_RESULT_SUCCESS; + * dbus_some_function (arg1, arg2, &result); + * if (result != DBUS_RESULT_SUCCESS) + *   printf ("an error occurred\n"); + * @endcode + *  + * @{ + */ + +/** + * Set a result code at a result code location, + * if code_address is not #NULL. + * + * @param code_address place to store the result code. + * @param code the result code itself. + */ +void +dbus_set_result (DBusResultCode *code_address, +                 DBusResultCode  code) +{ +  if (code_address) +    *code_address = code; +} + +/** + * Returns a string describing the given result code. + * + * @param code the result code to describe. + * @returns a constant string describing the code. + */ +const char* +dbus_result_to_string (DBusResultCode code) +{ +  /* This is a switch to the compiler will complain if we +   * aren't handling some codes +   */ +  switch (code) +    { +    case DBUS_RESULT_SUCCESS: +      return "Success"; +    case DBUS_RESULT_FAILED: +      return "Unknown error"; +    case DBUS_RESULT_NO_MEMORY: +      return "Not enough memory available"; +    case DBUS_RESULT_IO_ERROR: +      return "Error reading or writing data"; +    case DBUS_RESULT_BAD_ADDRESS: +      return "Could not parse address"; +    case DBUS_RESULT_NOT_SUPPORTED: +      return "Feature not supported"; +    case DBUS_RESULT_LIMITS_EXCEEDED: +      return "Resource limits exceeded"; +    case DBUS_RESULT_ACCESS_DENIED: +      return "Permission denied"; +    case DBUS_RESULT_AUTH_FAILED: +      return "Could not authenticate to server"; +    case DBUS_RESULT_NO_SERVER: +      return "No server"; +    case DBUS_RESULT_TIMEOUT: +      return "Connection timed out"; +    case DBUS_RESULT_NO_NETWORK: +      return "Network unavailable"; +    case DBUS_RESULT_ADDRESS_IN_USE: +      return "Address already in use"; +    case DBUS_RESULT_DISCONNECTED: +      return "Disconnected."; +       +      /* no default, it would break our compiler warnings */ +    } + +  return "Invalid error code"; +} + +/** @} */ diff --git a/dbus/dbus-errors.h b/dbus/dbus-errors.h new file mode 100644 index 00000000..d45e2f37 --- /dev/null +++ b/dbus/dbus-errors.h @@ -0,0 +1,62 @@ +/* -*- mode: C; c-file-style: "gnu" -*- */ +/* dbus-errors.h Error reporting + * + * 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 + * + */ +#if !defined (DBUS_INSIDE_DBUS_H) && !defined (DBUS_COMPILATION) +#error "Only <dbus/dbus.h> can be included directly, this file may disappear or change contents." +#endif + +#ifndef DBUS_ERROR_H +#define DBUS_ERROR_H + +#include <dbus/dbus-macros.h> + +DBUS_BEGIN_DECLS; + +typedef enum +{ +  DBUS_RESULT_SUCCESS,         /**< Operation was successful. */ +  DBUS_RESULT_FAILED,          /**< Operation failed for unspecified reason. */ +  DBUS_RESULT_NO_MEMORY,       /**< Operation failed for lack of memory. */ +  DBUS_RESULT_IO_ERROR,        /**< Operation failed because of an IO error, +                                *   typically the other end closed the +                                *   connection. +                                */ +  DBUS_RESULT_BAD_ADDRESS,     /**< Address was bad, could not be parsed. */ +  DBUS_RESULT_NOT_SUPPORTED,   /**< Feature is not supported. */ +  DBUS_RESULT_LIMITS_EXCEEDED, /**< Some kernel resource limit exceeded. */ +  DBUS_RESULT_ACCESS_DENIED,   /**< Some sort of permissions/security problem. */ +  DBUS_RESULT_AUTH_FAILED,     /**< Could not authenticate. */ +  DBUS_RESULT_NO_SERVER,       /**< No one listening on the other end. */ +  DBUS_RESULT_TIMEOUT,         /**< Timed out trying to connect. */ +  DBUS_RESULT_NO_NETWORK,      /**< Can't find the network */ +  DBUS_RESULT_ADDRESS_IN_USE,  /**< Someone's already using the address */ +  DBUS_RESULT_DISCONNECTED     /**< No more connection. */ +} DBusResultCode; + +void        dbus_set_result       (DBusResultCode *code_address, +                                   DBusResultCode  code); +const char* dbus_result_to_string (DBusResultCode  code); + + +DBUS_END_DECLS; + +#endif /* DBUS_ERROR_H */ diff --git a/dbus/dbus-internals.c b/dbus/dbus-internals.c index 238baa89..e90703e4 100644 --- a/dbus/dbus-internals.c +++ b/dbus/dbus-internals.c @@ -23,6 +23,11 @@  #include "dbus-internals.h"  #include <stdio.h>  #include <stdarg.h> +#include <string.h> +#include <sys/types.h> +#include <errno.h> +#include <unistd.h> +#include <fcntl.h>  /**   * @defgroup DBusInternals D-BUS internal implementation details @@ -31,7 +36,7 @@   */  /** - * @defgroup DBusInternalsUtils Utilities + * @defgroup DBusInternalsUtils Utilities and portability   * @ingroup DBusInternals   * @brief Utility functions (_dbus_assert(), _dbus_warn(), etc.)   * @{ @@ -82,7 +87,13 @@   *   * @param integer the integer to stuff into a pointer.   */ - +/** + * @def _DBUS_ZERO + * + * Sets all bits in an object to zero. + * + * @param object the object to be zeroed. + */  /**   * @def _DBUS_INT_MIN   * @@ -93,6 +104,25 @@   *   * Maximum value of type "int"   */ +/** + * @def _DBUS_MAX_SUN_PATH_LENGTH + * + * Maximum length of the path to a UNIX domain socket, + * sockaddr_un::sun_path member. POSIX requires that all systems + * support at least 100 bytes here, including the nul termination. + * We use 99 for the max value to allow for the nul. + * + * We could probably also do sizeof (addr.sun_path) + * but this way we are the same on all platforms + * which is probably a good idea. + */ + +/** + * @typedef DBusForeachFunction + *  + * Used to iterate over each item in a collection, such as + * a DBusList. + */  /**   * Prints a warning message to stderr. @@ -112,6 +142,134 @@ _dbus_warn (const char *format,  }  /** + * Prints a warning message to stderr + * if the user has enabled verbose mode. + * + * @param format printf-style format string. + */ +void +_dbus_verbose (const char *format, +               ...) +{ +  va_list args; +  static dbus_bool_t verbose = TRUE; +  static dbus_bool_t initted = FALSE; + +  if (!verbose) +    return; +   +  if (!initted) +    { +      verbose = getenv ("DBUS_VERBOSE") != NULL; +      initted = TRUE; +    } +   +  va_start (args, format); +  vfprintf (stderr, format, args); +  va_end (args); +} + +/** + * A wrapper around strerror() because some platforms + * may be lame and not have strerror(). + * + * @param error_number errno. + * @returns error description. + */ +const char* +_dbus_strerror (int error_number) +{ +  return strerror (error_number); +} + +/** + * Converts a UNIX errno into a DBusResultCode. + * + * @param error_number the errno. + * @returns the result code. + */ +DBusResultCode +_dbus_result_from_errno (int error_number) +{ +  switch (error_number) +    { +    case 0: +      return DBUS_RESULT_SUCCESS; +       +#ifdef EPROTONOSUPPORT +    case EPROTONOSUPPORT: +      return DBUS_RESULT_NOT_SUPPORTED; +#endif +#ifdef EAFNOSUPPORT +    case EAFNOSUPPORT: +      return DBUS_RESULT_NOT_SUPPORTED; +#endif +#ifdef ENFILE +    case ENFILE: +      return DBUS_RESULT_LIMITS_EXCEEDED; /* kernel out of memory */ +#endif +#ifdef EMFILE +    case EMFILE: +      return DBUS_RESULT_LIMITS_EXCEEDED; +#endif +#ifdef EACCES +    case EACCES: +      return DBUS_RESULT_ACCESS_DENIED; +#endif +#ifdef EPERM +    case EPERM: +      return DBUS_RESULT_ACCESS_DENIED; +#endif +#ifdef ENOBUFS +    case ENOBUFS: +      return DBUS_RESULT_NO_MEMORY; +#endif +#ifdef ENOMEM +    case ENOMEM: +      return DBUS_RESULT_NO_MEMORY; +#endif +#ifdef EINVAL +    case EINVAL: +      return DBUS_RESULT_FAILED; +#endif +#ifdef EBADF +    case EBADF: +      return DBUS_RESULT_FAILED; +#endif +#ifdef EFAULT +    case EFAULT: +      return DBUS_RESULT_FAILED; +#endif +#ifdef ENOTSOCK +    case ENOTSOCK: +      return DBUS_RESULT_FAILED; +#endif +#ifdef EISCONN +    case EISCONN: +      return DBUS_RESULT_FAILED; +#endif +#ifdef ECONNREFUSED +    case ECONNREFUSED: +      return DBUS_RESULT_NO_SERVER; +#endif +#ifdef ETIMEDOUT +    case ETIMEDOUT: +      return DBUS_RESULT_TIMEOUT; +#endif +#ifdef ENETUNREACH +    case ENETUNREACH: +      return DBUS_RESULT_NO_NETWORK; +#endif +#ifdef EADDRINUSE +    case EADDRINUSE: +      return DBUS_RESULT_ADDRESS_IN_USE; +#endif       +    } + +  return DBUS_RESULT_FAILED; +} + +/**   * Duplicates a string. Result must be freed with   * dbus_free(). Returns #NULL if memory allocation fails.   * If the string to be duplicated is #NULL, returns #NULL. @@ -139,4 +297,38 @@ _dbus_strdup (const char *str)    return copy;  } +/** + * Sets a file descriptor to be nonblocking. + * + * @param fd the file descriptor. + * @param result address of result code. + * @returns #TRUE on success. + */ +dbus_bool_t +_dbus_set_fd_nonblocking (int             fd, +                          DBusResultCode *result) +{ +  int val; + +  val = fcntl (fd, F_GETFL, 0); +  if (val < 0) +    { +      dbus_set_result (result, _dbus_result_from_errno (errno)); +      _dbus_verbose ("Failed to get flags for fd %d: %s\n", fd, +                     _dbus_strerror (errno)); +      return FALSE; +    } + +  if (fcntl (fd, F_SETFL, val | O_NONBLOCK) < 0) +    { +      dbus_set_result (result, _dbus_result_from_errno (errno));       +      _dbus_verbose ("Failed to set fd %d nonblocking: %s\n", +                     fd, _dbus_strerror (errno)); + +      return FALSE; +    } + +  return TRUE; +} +  /** @} */ diff --git a/dbus/dbus-internals.h b/dbus/dbus-internals.h index 74bae96e..5d001397 100644 --- a/dbus/dbus-internals.h +++ b/dbus/dbus-internals.h @@ -20,6 +20,9 @@   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA   *   */ +#ifdef DBUS_INSIDE_DBUS_H +#error "You can't include dbus-internals.h in the public header dbus.h" +#endif  #ifndef DBUS_INTERNALS_H  #define DBUS_INTERNALS_H @@ -28,13 +31,20 @@  #include <dbus/dbus-memory.h>  #include <dbus/dbus-types.h> +#include <dbus/dbus-errors.h>  #include <stdlib.h> /* for abort() */  #include <string.h> /* just so it's there in every file */  DBUS_BEGIN_DECLS; -void _dbus_warn (const char *format, -                 ...); +void _dbus_warn    (const char *format, +                    ...); +void _dbus_verbose (const char *format, +                    ...); + +const char* _dbus_strerror (int error_number); + +DBusResultCode _dbus_result_from_errno (int error_number);  #define _dbus_assert(condition)                                         \  do {                                                                    \ @@ -58,10 +68,19 @@ do {  #define _DBUS_POINTER_TO_INT(pointer) ((long)(pointer))  #define _DBUS_INT_TO_POINTER(integer) ((void*)((long)(integer))) +#define _DBUS_ZERO(object) (memset (&(object), '\0', sizeof ((object)))) +  char* _dbus_strdup (const char *str);  #define _DBUS_INT_MIN	(-_DBUS_INT_MAX - 1)  #define _DBUS_INT_MAX	2147483647 +#define _DBUS_MAX_SUN_PATH_LENGTH 99 + +typedef void (* DBusForeachFunction) (void *element, +                                      void *data); + +dbus_bool_t _dbus_set_fd_nonblocking (int             fd, +                                      DBusResultCode *result);  DBUS_END_DECLS; diff --git a/dbus/dbus-list.c b/dbus/dbus-list.c index 0b8e5a4e..31e5ae3f 100644 --- a/dbus/dbus-list.c +++ b/dbus/dbus-list.c @@ -521,6 +521,34 @@ _dbus_list_get_length (DBusList **list)    return length;  } +/** + * Calls the given function for each element in the list.  The + * function is passed the list element as its first argument, and the + * given data as its second argument. + * + * @param list address of the head of the list. + * @param function function to call for each element. + * @param data extra data for the function. + *  + */ +void +_dbus_list_foreach (DBusList          **list, +                    DBusForeachFunction function, +                    void               *data) +{ +  DBusList *link; + +  link = *list; +  while (link != NULL) +    { +      DBusList *next = _dbus_list_get_next_link (list, link); +       +      (* function) (link->data, data); +       +      link = next; +    } +} +  /** @} */  #ifdef DBUS_BUILD_TESTS diff --git a/dbus/dbus-list.h b/dbus/dbus-list.h index d314ad5a..8de40002 100644 --- a/dbus/dbus-list.h +++ b/dbus/dbus-list.h @@ -24,6 +24,7 @@  #ifndef DBUS_LIST_H  #define DBUS_LIST_H +#include <dbus/dbus-internals.h>  #include <dbus/dbus-memory.h>  #include <dbus/dbus-types.h> @@ -63,7 +64,9 @@ dbus_bool_t _dbus_list_copy           (DBusList **list,                                         DBusList **dest);  int         _dbus_list_get_length     (DBusList **list); - +void _dbus_list_foreach (DBusList            **list, +                         DBusForeachFunction   function, +                         void                 *data);  #define _dbus_list_get_next_link(list, link) ((link)->next == *(list) ? NULL : (link)->next)  #define _dbus_list_get_prev_link(list, link) ((link)->prev == *(list) ? NULL : (link)->prev) diff --git a/dbus/dbus-message-internal.h b/dbus/dbus-message-internal.h new file mode 100644 index 00000000..5b1aea66 --- /dev/null +++ b/dbus/dbus-message-internal.h @@ -0,0 +1,56 @@ +/* -*- mode: C; c-file-style: "gnu" -*- */ +/* dbus-message-internal.h DBusMessage object internal interfaces + * + * 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 + * + */ +#ifndef DBUS_MESSAGE_INTERNAL_H +#define DBUS_MESSAGE_INTERNAL_H + +#include <dbus/dbus-message.h> + +DBUS_BEGIN_DECLS; + +typedef struct DBusMessageLoader DBusMessageLoader; + +void _dbus_message_get_network_data (DBusMessage          *message, +                                     const unsigned char **header, +                                     int                  *header_len, +                                     const unsigned char **body, +                                     int                  *body_len); + +void _dbus_message_lock             (DBusMessage          *message); + + +DBusMessageLoader* _dbus_message_loader_new                   (void); +void               _dbus_message_loader_ref                   (DBusMessageLoader  *loader); +void               _dbus_message_loader_unref                 (DBusMessageLoader  *loader); +dbus_bool_t        _dbus_message_loader_get_buffer            (DBusMessageLoader  *loader, +                                                               unsigned char     **buffer, +                                                               int                *buffer_len); +void               _dbus_message_loader_return_buffer         (DBusMessageLoader  *loader, +                                                               unsigned char      *buffer, +                                                               int                 bytes_read); + +DBusMessage*       _dbus_message_loader_pop_message           (DBusMessageLoader  *loader); + + +DBUS_END_DECLS; + +#endif /* DBUS_MESSAGE_H */ diff --git a/dbus/dbus-message.c b/dbus/dbus-message.c index a52d7097..1e95bec7 100644 --- a/dbus/dbus-message.c +++ b/dbus/dbus-message.c @@ -21,7 +21,12 @@   *   */ +#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 @@ -43,17 +48,72 @@  struct DBusMessage  {    int refcount; /**< Reference count */ -   + +  unsigned char *header; /**< Header network data, stored +                          * separately from body so we can +                          * independently realloc it. +                          */ +  int header_len;        /**< Length of header data. */ + +  unsigned char *body;   /**< Body network data. */ +  int body_len;          /**< Length of body 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 header_len return location for header length in bytes. + * @param body return location for message body data. + * @param body_len return location for body length in bytes. + */ +void +_dbus_message_get_network_data (DBusMessage          *message, +                                const unsigned char **header, +                                int                  *header_len, +                                const unsigned char **body, +                                int                  *body_len) +{ +  _dbus_assert (message->locked); +   +  *header = message->header; +  *header_len = message->header_len; +  *body = message->body; +  *body_len = message->body_len; +} + +/** + * 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 DBusMessage object + * @brief Message to be sent or received over a DBusConnection.   * - * Types and functions related to the DBusMessage object. + * 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.   *   * @{   */ @@ -66,15 +126,30 @@ struct DBusMessage   */  /** - * Constructs a new message. + * 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; -  return NULL; +  message->refcount = 1; + +  /* We need to decide what a message contains. ;-) */ +  message->header = _dbus_strdup ("H"); +  message->header_len = 2; +  message->body = _dbus_strdup ("Body"); +  message->body_len = 5; +   +  return message;  } @@ -87,7 +162,7 @@ dbus_message_new (void)  void  dbus_message_ref (DBusMessage *message)  { -   +  message->refcount += 1;  }  /** @@ -99,8 +174,246 @@ dbus_message_ref (DBusMessage *message)  void  dbus_message_unref (DBusMessage *message)  { +  _dbus_assert (message != NULL); +  _dbus_assert (message->refcount > 0); + +  message->refcount -= 1; +  if (message->refcount == 0) +    { +       +      dbus_free (message); +    } +} + +/** @} */ + +/** + * @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. */ +   +  int allocated;       /**< Allocated size of "data" */ +  int length;          /**< Used size of "data" */ + +  unsigned char *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;   +   +  /* Header, plus room for averagish other fields */ +  loader->allocated = INITIAL_LOADER_DATA_LEN; +  loader->data = dbus_malloc (loader->allocated); +  if (loader->data == NULL) +    loader->allocated = 0; +   +  loader->length = 0; +   +  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_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 address to store the buffer. + * @param buffer_len address to store the buffer length. + * @returns #FALSE if no buffer can be allocated. + */ +dbus_bool_t +_dbus_message_loader_get_buffer (DBusMessageLoader  *loader, +                                 unsigned char     **buffer, +                                 int                *buffer_len) +{ +  _dbus_assert (!loader->buffer_outstanding); +   +#define MIN_BUFSIZE INITIAL_LOADER_DATA_LEN +   +  if ((loader->length + MIN_BUFSIZE) >= loader->allocated) +    { +      unsigned char *buf; +      int new_allocated; + +      /* double (and add MIN_BUFSIZE, in case allocated == 0) */ +      new_allocated = MIN_BUFSIZE + loader->allocated * 2; + +      if (new_allocated <= loader->allocated) +        { +          /* ugh, overflow. Maybe someone is trying to screw us. */ +          /* (we could overflow so far that new_allocated > loader->allocated +           *  but nothing should break in that case) +           */ +          return FALSE; +        } +       +      buf = dbus_realloc (loader->data, new_allocated); +      if (buf == NULL) +        return FALSE; + +      loader->data = buf; +      loader->allocated = new_allocated; +    } + +  *buffer = loader->data + loader->length; +  *buffer_len = loader->allocated - loader->length; +   +  loader->buffer_outstanding = TRUE; +  return 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, +                                    unsigned char      *buffer, +                                    int                 bytes_read) +{ +  _dbus_assert (loader->buffer_outstanding); +  /* FIXME fake implementation just creates a message for every 7 +   * bytes. The real implementation will pass ownership of +   * loader->data 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->length += bytes_read; + +  loader->buffer_outstanding = FALSE; + +  while (loader->length >= 7) +    { +      DBusMessage *message; +       +      message = dbus_message_new (); +      if (message == NULL) +        break; /* ugh, postpone this I guess. */ + +      _dbus_list_append (&loader->messages, message); + +      memmove (loader->data, loader->data + 7, +               loader->length - 7); +      loader->length -= 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);  }  /** @} */ diff --git a/dbus/dbus-message.h b/dbus/dbus-message.h index ce618388..69d74e9c 100644 --- a/dbus/dbus-message.h +++ b/dbus/dbus-message.h @@ -28,6 +28,7 @@  #define DBUS_MESSAGE_H  #include <dbus/dbus-macros.h> +#include <dbus/dbus-types.h>  DBUS_BEGIN_DECLS; diff --git a/dbus/dbus-server-protected.h b/dbus/dbus-server-protected.h new file mode 100644 index 00000000..fb57c2ee --- /dev/null +++ b/dbus/dbus-server-protected.h @@ -0,0 +1,79 @@ +/* -*- mode: C; c-file-style: "gnu" -*- */ +/* dbus-server-protected.h Used by subclasses of DBusServer object (internal to D-BUS implementation) + * + * 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 + * + */ +#ifndef DBUS_SERVER_PROTECTED_H +#define DBUS_SERVER_PROTECTED_H + +#include <dbus/dbus-internals.h> +#include <dbus/dbus-server.h> +#include <dbus/dbus-watch.h> + +DBUS_BEGIN_DECLS; + +typedef struct DBusServerVTable DBusServerVTable; + +struct DBusServerVTable +{ +  void (* finalize)      (DBusServer *server); +  /**< The finalize method must free the server. */ + +  void (* handle_watch)  (DBusServer  *server, +                          DBusWatch   *watch, +                          unsigned int flags); +  /**< The handle_watch method handles reading/writing +   * data as indicated by the flags. +   */ +   +  void (* disconnect)    (DBusServer *server); +  /**< Disconnect this server. */ +}; + +struct DBusServer +{ +  int refcount;                               /**< Reference count. */ +  const DBusServerVTable *vtable;             /**< Virtual methods for this instance. */ +  DBusWatchList *watches; +   +  DBusNewConnectionFunction  new_connection_function; +  /**< Callback to invoke when a new connection is created. */ +  void *new_connection_data; +  /**< Data for new connection callback */ +  DBusFreeFunction new_connection_free_data_function; +  /**< Callback to invoke to free new_connection_data +   * when server is finalized or data is replaced. +   */ +   +  unsigned int disconnected : 1;              /**< TRUE if we are disconnected. */ +}; + +dbus_bool_t  _dbus_server_init_base     (DBusServer             *server, +                                        const DBusServerVTable *vtable); +void        _dbus_server_finalize_base (DBusServer             *server); +dbus_bool_t _dbus_server_add_watch     (DBusServer             *server, +                                        DBusWatch              *watch); +void        _dbus_server_remove_watch  (DBusServer             *server, +                                        DBusWatch              *watch); + + +DBUS_END_DECLS; + +#endif /* DBUS_SERVER_PROTECTED_H */ diff --git a/dbus/dbus-server-unix.c b/dbus/dbus-server-unix.c new file mode 100644 index 00000000..277e00a6 --- /dev/null +++ b/dbus/dbus-server-unix.c @@ -0,0 +1,303 @@ +/* -*- mode: C; c-file-style: "gnu" -*- */ +/* dbus-server-unix.c Server implementation for Unix network protocols. + * + * 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-server-unix.h" +#include "dbus-transport-unix.h" +#include "dbus-connection-internal.h" +#include <sys/types.h> +#include <unistd.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <errno.h> +#include <fcntl.h> + +/** + * @defgroup DBusServerUnix DBusServer implementations for UNIX + * @ingroup  DBusInternals + * @brief Implementation details of DBusServer on UNIX + * + * @{ + */ +/** + *  + * Opaque object representing a Unix server implementation. + */ +typedef struct DBusServerUnix DBusServerUnix; + +/** + * Implementation details of DBusServerUnix. All members + * are private. + */ +struct DBusServerUnix +{ +  DBusServer base;  /**< Parent class members. */ +  int fd;           /**< File descriptor or -1 if disconnected. */ +  DBusWatch *watch; /**< File descriptor watch. */ +}; + +static void +unix_finalize (DBusServer *server) +{ +  DBusServerUnix *unix_server = (DBusServerUnix*) server; +   +  _dbus_server_finalize_base (server); + +  if (unix_server->watch) +    _dbus_watch_unref (unix_server->watch); +   +  dbus_free (server); +} + +static void +handle_new_client_fd (DBusServer *server, +                      int         client_fd) +{ +  DBusConnection *connection; +  DBusTransport *transport; +   +  _dbus_verbose ("Creating new client connection with fd %d\n", client_fd); +           +  if (!_dbus_set_fd_nonblocking (client_fd, NULL)) +    return; +   +  transport = _dbus_transport_new_for_fd (client_fd); +  if (transport == NULL) +    { +      close (client_fd); +      return; +    } + +  /* note that client_fd is now owned by the transport, and will be +   * closed on transport disconnection/finalization +   */ +   +  connection = _dbus_connection_new_for_transport (transport); +  _dbus_transport_unref (transport); +   +  if (connection == NULL) +    return; + +  /* See if someone wants to handle this new connection, +   * self-referencing for paranoia +   */ +  if (server->new_connection_function) +    { +      dbus_server_ref (server); +       +      (* server->new_connection_function) (server, connection, +                                           server->new_connection_data); +      dbus_server_unref (server); +    } +   +  /* If no one grabbed a reference, the connection will die. */ +  dbus_connection_unref (connection); +} + +static void +unix_handle_watch (DBusServer  *server, +                   DBusWatch   *watch, +                   unsigned int flags) +{ +  DBusServerUnix *unix_server = (DBusServerUnix*) server; + +  _dbus_assert (watch == unix_server->watch); + +  _dbus_verbose ("Handling client connection, flags 0x%x\n", flags); +   +  if (flags & DBUS_WATCH_READABLE) +    { +      int client_fd; +      int listen_fd; +       +      listen_fd = dbus_watch_get_fd (watch); + +    retry: +      client_fd = accept (listen_fd, NULL, NULL); +       +      if (client_fd < 0) +        { +          if (errno == EINTR) +            goto retry; +          else if (errno == EAGAIN || errno == EWOULDBLOCK) +            _dbus_verbose ("No client available to accept after all\n"); +          else +            _dbus_verbose ("Failed to accept a client connection: %s\n", +                           _dbus_strerror (errno)); +        } +      else +        { +          handle_new_client_fd (server, client_fd); +        } +    } + +  if (flags & DBUS_WATCH_ERROR) +    _dbus_verbose ("Error on server listening socket\n"); + +  if (flags & DBUS_WATCH_HANGUP) +    _dbus_verbose ("Hangup on server listening socket\n"); +} +   +static void +unix_disconnect (DBusServer *server) +{ +  DBusServerUnix *unix_server = (DBusServerUnix*) server; + +  if (unix_server->watch) +    { +      _dbus_server_remove_watch (server, +                                 unix_server->watch); +      _dbus_watch_unref (unix_server->watch); +      unix_server->watch = NULL; +    } +   +  close (unix_server->fd); +  unix_server->fd = -1; +} + +static DBusServerVTable unix_vtable = { +  unix_finalize, +  unix_handle_watch, +  unix_disconnect +}; + +/** + * Creates a new server listening on the given file descriptor.  The + * file descriptor should be nonblocking (use + * _dbus_set_fd_nonblocking() to make it so). The file descriptor + * should be listening for connections, that is, listen() should have + * been successfully invoked on it. The server will use accept() to + * accept new client connections. + * + * @param fd the file descriptor. + * @returns the new server, or #NULL if no memory. + *  + */ +DBusServer* +_dbus_server_new_for_fd (int fd) +{ +  DBusServerUnix *unix_server; +  DBusWatch *watch; + +  watch = _dbus_watch_new (fd, +                           DBUS_WATCH_READABLE); +  if (watch == NULL) +    return NULL; +   +  unix_server = dbus_new0 (DBusServerUnix, 1); +  if (unix_server == NULL) +    { +      _dbus_watch_unref (watch); +      return NULL; +    } +   +  if (!_dbus_server_init_base (&unix_server->base, +                               &unix_vtable)) +    { +      _dbus_watch_unref (watch); +      dbus_free (unix_server); +      return NULL; +    } + +  if (!_dbus_server_add_watch (&unix_server->base, +                               watch)) +    { +      _dbus_server_finalize_base (&unix_server->base); +      _dbus_watch_unref (watch); +      dbus_free (unix_server); +      return NULL; +    } +   +  unix_server->fd = fd; +  unix_server->watch = watch; + +  return (DBusServer*) unix_server; +} + +/** + * Creates a new server listening on the given Unix domain socket. + * + * @param path the path for the domain socket. + * @param result location to store reason for failure. + * @returns the new server, or #NULL on failure. + */ +DBusServer* +_dbus_server_new_for_domain_socket (const char     *path, +                                    DBusResultCode *result) +{ +  DBusServer *server; +  int listen_fd; +  struct sockaddr_un addr; + +  listen_fd = socket (AF_LOCAL, SOCK_STREAM, 0); + +  if (listen_fd < 0) +    { +      dbus_set_result (result, _dbus_result_from_errno (errno)); +      _dbus_verbose ("Failed to create socket \"%s\": %s\n", +                     path, _dbus_strerror (errno)); +      return NULL; +    } + +  if (!_dbus_set_fd_nonblocking (listen_fd, result)) +    { +      close (listen_fd); +      return NULL; +    } + +  _DBUS_ZERO (addr); +  addr.sun_family = AF_LOCAL; +  strncpy (addr.sun_path, path, _DBUS_MAX_SUN_PATH_LENGTH); +  addr.sun_path[_DBUS_MAX_SUN_PATH_LENGTH] = '\0'; +   +  if (bind (listen_fd, (struct sockaddr*) &addr, SUN_LEN (&addr)) < 0) +    { +      dbus_set_result (result, _dbus_result_from_errno (errno)); +      _dbus_verbose ("Failed to bind socket \"%s\": %s\n", +                     path, _dbus_strerror (errno)); +      close (listen_fd); +      return NULL; +    } + +  if (listen (listen_fd, 30 /* backlog */) < 0) +    { +      dbus_set_result (result, _dbus_result_from_errno (errno));       +      _dbus_verbose ("Failed to listen on socket \"%s\": %s\n", +                     path, _dbus_strerror (errno)); +      close (listen_fd); +      return NULL; +    } +   +  server = _dbus_server_new_for_fd (listen_fd); +  if (server == NULL) +    { +      dbus_set_result (result, DBUS_RESULT_NO_MEMORY); +      close (listen_fd); +      return NULL; +    } + +  return server; +} + +/** @} */ + diff --git a/dbus/dbus-server-unix.h b/dbus/dbus-server-unix.h new file mode 100644 index 00000000..43614cc0 --- /dev/null +++ b/dbus/dbus-server-unix.h @@ -0,0 +1,37 @@ +/* -*- mode: C; c-file-style: "gnu" -*- */ +/* dbus-server-unix.h Server implementation for Unix network protocols. + * + * 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 + * + */ +#ifndef DBUS_SERVER_UNIX_H +#define DBUS_SERVER_UNIX_H + +#include <dbus/dbus-internals.h> +#include <dbus/dbus-server-protected.h> + +DBUS_BEGIN_DECLS; + +DBusServer* _dbus_server_new_for_fd            (int             fd); +DBusServer* _dbus_server_new_for_domain_socket (const char     *path, +                                                DBusResultCode *result); + +DBUS_END_DECLS; + +#endif /* DBUS_SERVER_UNIX_H */ diff --git a/dbus/dbus-server.c b/dbus/dbus-server.c new file mode 100644 index 00000000..c4b36bd6 --- /dev/null +++ b/dbus/dbus-server.c @@ -0,0 +1,299 @@ +/* -*- mode: C; c-file-style: "gnu" -*- */ +/* dbus-server.c DBusServer 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-server.h" +#include "dbus-server-unix.h" + +/** + * @defgroup DBusServer DBusServer + * @ingroup  DBus + * @brief Server that listens for new connections. + * + * Types and functions related to DBusServer. + * A DBusServer represents a server that other applications + * can connect to. Each connection from another application + * is represented by a DBusConnection. + */ + +/** + * @defgroup DBusServerInternals DBusServer implementation details + * @ingroup  DBusInternals + * @brief Implementation details of DBusServer + * + * @{ + */ + +/** + * Initializes the members of the DBusServer base class. + * Chained up to by subclass constructors. + * + * @param server the server. + * @param vtable the vtable for the subclass. + * @returns #TRUE on success. + */ +dbus_bool_t +_dbus_server_init_base (DBusServer             *server, +                        const DBusServerVTable *vtable) +{ +  server->vtable = vtable; +  server->refcount = 1; + +  server->watches = _dbus_watch_list_new (); +  if (server->watches == NULL) +    return FALSE; + +  return TRUE; +} + +/** + * Finalizes the members of the DBusServer base class. + * Chained up to by subclass finalizers. + * + * @param server the server. + */ +void +_dbus_server_finalize_base (DBusServer *server) +{ +  dbus_server_set_new_connection_function (server, NULL, NULL, NULL); + +  if (!server->disconnected) +    dbus_server_disconnect (server); + +  _dbus_watch_list_free (server->watches); +} + +/** + * Adds a watch for this server, chaining out to application-provided + * watch handlers. + * + * @param server the server. + * @param watch the watch to add. + */ +dbus_bool_t +_dbus_server_add_watch (DBusServer *server, +                        DBusWatch  *watch) +{ +  return _dbus_watch_list_add_watch (server->watches, watch); +} + +/** + * Removes a watch previously added with _dbus_server_remove_watch(). + * + * @param server the server. + * @param watch the watch to remove. + */ +void +_dbus_server_remove_watch  (DBusServer *server, +                            DBusWatch  *watch) +{ +  _dbus_watch_list_remove_watch (server->watches, watch); +} + + +/** @} */ + +/** + * @addtogroup DBusServer + * + * @{ + */ + + +/** + * @typedef DBusServer + * + * An opaque object representing a server that listens for + * connections from other applications. Each time a connection + * is made, a new DBusConnection is created and made available + * via an application-provided DBusNewConnectionFunction. + * The DBusNewConnectionFunction is provided with + * dbus_server_set_new_connection_function(). + *  + */ + +/** + * Listens for new connections on the given address. + * Returns #NULL if listening fails for any reason. + * Otherwise returns a new #DBusServer. + * dbus_server_set_new_connection_function() and + * dbus_server_set_watch_functions() should be called + * immediately to render the server fully functional. + * + * @param address the address of this server. + * @param result location to store rationale for failure. + * @returns a new DBusServer, or #NULL on failure. + *  + */ +DBusServer* +dbus_server_listen (const char     *address, +                    DBusResultCode *result) +{ +  DBusServer *server; + +  /* For now just pretend the address is a unix domain socket path */ +  server = _dbus_server_new_for_domain_socket (address, result); +   +  return server; +} + +/** + * Increments the reference count of a DBusServer. + * + * @param server the server. + */ +void +dbus_server_ref (DBusServer *server) +{ +  server->refcount += 1; +} + +/** + * Decrements the reference count of a DBusServer.  Finalizes the + * server if the reference count reaches zero. The server connection + * will be closed as with dbus_server_disconnect() when the server is + * finalized. + * + * @param server the server. + */ +void +dbus_server_unref (DBusServer *server) +{ +  _dbus_assert (server != NULL); +  _dbus_assert (server->refcount > 0); + +  server->refcount -= 1; +  if (server->refcount == 0) +    { +      _dbus_assert (server->vtable->finalize != NULL); +       +      (* server->vtable->finalize) (server); +    } +} + +/** + * Releases the server's address and stops listening for + * new clients. If called more than once, only the first + * call has an effect. Does not modify the server's + * reference count. + *  + * @param server the server. + */ +void +dbus_server_disconnect (DBusServer *server) +{ +  _dbus_assert (server->vtable->disconnect != NULL); + +  if (server->disconnected) +    return; +   +  (* server->vtable->disconnect) (server); +  server->disconnected = TRUE; +} + +/** + * Returns #TRUE if the server is still listening for new connections. + * + * @param server the server. + */ +dbus_bool_t +dbus_server_get_is_connected (DBusServer *server) +{ +  return !server->disconnected; +} + +/** + * Sets a function to be used for handling new connections.  The given + * function is passed each new connection as the connection is + * created. If the new connection function increments the connection's + * reference count, the connection will stay alive. Otherwise, the + * connection will be unreferenced and closed. + * + * @param server the server. + * @param function a function to handle new connections. + * @param data data to pass to the new connection handler. + * @param free_data_function function to free the data. + */ +void +dbus_server_set_new_connection_function (DBusServer                *server, +                                         DBusNewConnectionFunction  function, +                                         void                      *data, +                                         DBusFreeFunction           free_data_function) +{ +  if (server->new_connection_free_data_function != NULL) +    (* server->new_connection_free_data_function) (server->new_connection_data); +   +  server->new_connection_function = function; +  server->new_connection_data = data; +  server->new_connection_free_data_function = free_data_function; +} + +/** + * Sets the watch functions for the connection. These functions are + * responsible for making the application's main loop aware of file + * descriptors that need to be monitored for events. + * + * This function behaves exactly like dbus_connection_set_watch_functions(); + * see the documentation for that routine. + * + * @param server the server. + * @param add_function function to begin monitoring a new descriptor. + * @param remove_function function to stop monitoring a descriptor. + * @param data data to pass to add_function and remove_function. + * @param free_data_function function to be called to free the data. + */ +void +dbus_server_set_watch_functions (DBusServer              *server, +                                 DBusAddWatchFunction     add_function, +                                 DBusRemoveWatchFunction  remove_function, +                                 void                    *data, +                                 DBusFreeFunction         free_data_function) +{ +  _dbus_watch_list_set_functions (server->watches, +                                  add_function, +                                  remove_function, +                                  data, +                                  free_data_function); +} + +/** + * Called to notify the server when a previously-added watch + * is ready for reading or writing, or has an exception such + * as a hangup. + * + * @param server the server. + * @param watch the watch. + * @param condition the current condition of the file descriptors being watched. + */ +void +dbus_server_handle_watch (DBusServer              *server, +                          DBusWatch               *watch, +                          unsigned int             condition) +{ +  _dbus_assert (server->vtable->handle_watch != NULL); + +  _dbus_watch_sanitize_condition (watch, &condition); +   +  (* server->vtable->handle_watch) (server, watch, condition); +} + +/** @} */ + diff --git a/dbus/dbus-server.h b/dbus/dbus-server.h new file mode 100644 index 00000000..83f76100 --- /dev/null +++ b/dbus/dbus-server.h @@ -0,0 +1,66 @@ +/* -*- mode: C; c-file-style: "gnu" -*- */ +/* dbus-server.h DBusServer 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 + * + */ +#if !defined (DBUS_INSIDE_DBUS_H) && !defined (DBUS_COMPILATION) +#error "Only <dbus/dbus.h> can be included directly, this file may disappear or change contents." +#endif + +#ifndef DBUS_SERVER_H +#define DBUS_SERVER_H + +#include <dbus/dbus-errors.h> +#include <dbus/dbus-message.h> +#include <dbus/dbus-connection.h> + +DBUS_BEGIN_DECLS; + +typedef struct DBusServer DBusServer; + +typedef void (* DBusNewConnectionFunction) (DBusServer     *server, +                                            DBusConnection *new_connection, +                                            void           *data); + +DBusServer* dbus_server_listen           (const char     *address, +                                          DBusResultCode *result); +void        dbus_server_ref              (DBusServer     *server); +void        dbus_server_unref            (DBusServer     *server); +void        dbus_server_disconnect       (DBusServer     *server); +dbus_bool_t dbus_server_get_is_connected (DBusServer     *server); + +void dbus_server_set_new_connection_function (DBusServer                *server, +                                              DBusNewConnectionFunction  function, +                                              void                      *data, +                                              DBusFreeFunction           free_data_function); +void dbus_server_set_watch_functions         (DBusServer                *server, +                                              DBusAddWatchFunction       add_function, +                                              DBusRemoveWatchFunction    remove_function, +                                              void                      *data, +                                              DBusFreeFunction           free_data_function); +void dbus_server_handle_watch                (DBusServer                *server, +                                              DBusWatch                 *watch, +                                              unsigned int               condition); + + + +DBUS_END_DECLS; + +#endif /* DBUS_SERVER_H */ diff --git a/dbus/dbus-transport-protected.h b/dbus/dbus-transport-protected.h new file mode 100644 index 00000000..e6c696f0 --- /dev/null +++ b/dbus/dbus-transport-protected.h @@ -0,0 +1,88 @@ +/* -*- mode: C; c-file-style: "gnu" -*- */ +/* dbus-transport-protected.h Used by subclasses of DBusTransport object (internal to D-BUS implementation) + * + * 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 + * + */ +#ifndef DBUS_TRANSPORT_PROTECTED_H +#define DBUS_TRANSPORT_PROTECTED_H + +#include <dbus/dbus-internals.h> +#include <dbus/dbus-errors.h> +#include <dbus/dbus-transport.h> +#include <dbus/dbus-message-internal.h> + +DBUS_BEGIN_DECLS; + +typedef struct DBusTransportVTable DBusTransportVTable; + +struct DBusTransportVTable +{ +  void (* finalize)           (DBusTransport *transport); +  /**< The finalize method must free the transport. */ + +  void (* handle_watch)       (DBusTransport *transport, +                               DBusWatch     *watch, +                               unsigned int   flags); +  /**< The handle_watch method handles reading/writing +   * data as indicated by the flags. +   */ + +  void (* disconnect)         (DBusTransport *transport); +  /**< Disconnect this transport. */ + +  void (* connection_set)     (DBusTransport *transport); +  /**< Called when transport->connection has been filled in */ + +  void (* messages_pending)   (DBusTransport *transport, +                               int            queue_length); +  /**< Called when the outgoing message queue goes from empty +   * to non-empty or vice versa. +   */ + +  void (* do_iteration)       (DBusTransport *transport, +                               unsigned int   flags, +                               int            timeout_milliseconds); +  /**< Called to do a single "iteration" (block on select/poll +   * followed by reading or writing data). +   */ +}; + +struct DBusTransport +{ +  int refcount;                               /**< Reference count. */ + +  const DBusTransportVTable *vtable;          /**< Virtual methods for this instance. */ + +  DBusConnection *connection;                 /**< Connection owning this transport. */ + +  DBusMessageLoader *loader;                  /**< Message-loading buffer. */ +   +  unsigned int disconnected : 1;              /**< TRUE if we are disconnected. */ +}; + +dbus_bool_t _dbus_transport_init_base     (DBusTransport             *transport, +                                           const DBusTransportVTable *vtable); +void        _dbus_transport_finalize_base (DBusTransport             *transport); + + + +DBUS_END_DECLS; + +#endif /* DBUS_TRANSPORT_PROTECTED_H */ diff --git a/dbus/dbus-transport-unix.c b/dbus/dbus-transport-unix.c new file mode 100644 index 00000000..869aa33f --- /dev/null +++ b/dbus/dbus-transport-unix.c @@ -0,0 +1,581 @@ +/* -*- mode: C; c-file-style: "gnu" -*- */ +/* dbus-transport-unix.c UNIX socket subclasses of DBusTransport + * + * 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-connection-internal.h" +#include "dbus-transport-unix.h" +#include "dbus-transport-protected.h" +#include "dbus-watch.h" +#include <sys/types.h> +#include <unistd.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <errno.h> +#include <fcntl.h> +#ifdef HAVE_WRITEV +#include <sys/uio.h> +#endif + +/** + * @defgroup DBusTransportUnix DBusTransport implementations for UNIX + * @ingroup  DBusInternals + * @brief Implementation details of DBusTransport on UNIX + * + * @{ + */ + +/** + * Opaque object representing a Unix file descriptor transport. + */ +typedef struct DBusTransportUnix DBusTransportUnix; + +/** + * Implementation details of DBusTransportUnix. All members are private. + */ +struct DBusTransportUnix +{ +  DBusTransport base;                   /**< Parent instance */ +  int fd;                               /**< File descriptor. */ +  DBusWatch *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. */ + +  int message_bytes_written;            /**< Number of bytes of current +                                         *   outgoing message that have +                                         *   been written. +                                         */ +}; + +static void +unix_finalize (DBusTransport *transport) +{ +  DBusTransportUnix *unix_transport = (DBusTransportUnix*) transport; +   +  _dbus_transport_finalize_base (transport); + +  if (unix_transport->watch) +    { +      _dbus_watch_invalidate (unix_transport->watch); +      _dbus_watch_unref (unix_transport->watch); +    } +   +  dbus_free (transport); +} + +static void +do_io_error (DBusTransport *transport) +{ +  _dbus_transport_disconnect (transport); +  _dbus_connection_transport_error (transport->connection, +                                    DBUS_RESULT_DISCONNECTED); +} + +static void +do_writing (DBusTransport *transport) +{ +  int total; +  DBusTransportUnix *unix_transport = (DBusTransportUnix*) transport; +   +  total = 0; +   + again: + +  while (_dbus_connection_have_messages_to_send (transport->connection)) +    { +      int bytes_written; +      DBusMessage *message; +      const unsigned char *header; +      const unsigned char *body; +      int header_len, body_len; +       +      if (total > unix_transport->max_bytes_written_per_iteration) +        { +          _dbus_verbose ("%d bytes exceeds %d bytes written per iteration, returning\n", +                         total, unix_transport->max_bytes_written_per_iteration); +          goto out; +        } +       +      message = _dbus_connection_get_message_to_send (transport->connection); +      _dbus_assert (message != NULL); +      _dbus_message_lock (message); + +      _dbus_message_get_network_data (message, +                                      &header, &header_len, +                                      &body, &body_len); + +      if (unix_transport->message_bytes_written < header_len) +        { +#ifdef HAVE_WRITEV +          struct iovec vectors[2]; + +          vectors[0].iov_base = header + unix_transport->message_bytes_written; +          vectors[0].iov_len = header_len - unix_transport->message_bytes_written; +          vectors[1].iov_base = body; +          vectors[1].iov_len = body_len; +           +          bytes_written = writev (unix_transport->fd, +                                  vectors, _DBUS_N_ELEMENTS (vectors)); +#else +          bytes_written = write (unix_transport->fd, +                                 header + unix_transport->message_bytes_written,  +                                 header_len - unix_transport->message_bytes_written); +#endif +        } +      else +        { +          bytes_written = write (unix_transport->fd, +                                 body + +                                 (unix_transport->message_bytes_written - header_len),  +                                 body_len - +                                 (unix_transport->message_bytes_written - body_len)); +        } + +      if (bytes_written < 0) +        { +          if (errno == EINTR) +            goto again; +          else if (errno == EAGAIN || +                   errno == EWOULDBLOCK) +            goto out; +          else +            { +              _dbus_verbose ("Error writing to message bus: %s\n", +                             _dbus_strerror (errno)); +              do_io_error (transport); +              goto out; +            } +        } +      else +        {           +          _dbus_verbose (" wrote %d bytes\n", bytes_written); +           +          total += bytes_written; +          unix_transport->message_bytes_written += bytes_written; + +          _dbus_assert (unix_transport->message_bytes_written <= +                        (header_len + body_len)); +           +          if (unix_transport->message_bytes_written == (header_len + body_len)) +            { +              _dbus_connection_message_sent (transport->connection, +                                             message); +              unix_transport->message_bytes_written = 0; +            } +        } +    } + + out: +  return; /* I think some C compilers require a statement after a label */ +} + +static void +do_reading (DBusTransport *transport) +{ +  DBusTransportUnix *unix_transport = (DBusTransportUnix*) transport; +  unsigned char *buffer; +  int buffer_len; +  int bytes_read; +  int total; +   +  total = 0; +   + again: + +  if (total > unix_transport->max_bytes_read_per_iteration) +    { +      _dbus_verbose ("%d bytes exceeds %d bytes read per iteration, returning\n", +                     total, unix_transport->max_bytes_read_per_iteration); +      goto out; +    } + +  if (!_dbus_message_loader_get_buffer (transport->loader, +                                        &buffer, &buffer_len)) +    goto out; /* no memory for a buffer */ +   +  bytes_read = read (unix_transport->fd, +                     buffer, buffer_len); + +  _dbus_message_loader_return_buffer (transport->loader, +                                      buffer, +                                      bytes_read < 0 ? 0 : bytes_read); +   +  if (bytes_read < 0) +    {       +      if (errno == EINTR) +        goto again; +      else if (errno == EAGAIN || +               errno == EWOULDBLOCK) +        goto out; +      else +        { +          _dbus_verbose ("Error reading from message bus: %s\n", +                         _dbus_strerror (errno)); +          do_io_error (transport); +          goto out; +        } +    } +  else if (bytes_read == 0) +    { +      _dbus_verbose ("Disconnected from message bus\n"); +      do_io_error (transport); +      goto out; +    } +  else +    { +      DBusMessage *message; +       +      _dbus_verbose (" read %d bytes\n", bytes_read); +       +      total += bytes_read;       + +      /* Queue any messages */ +      while ((message = _dbus_message_loader_pop_message (transport->loader))) +        { +          _dbus_verbose ("queueing received message %p\n", message); +           +          _dbus_connection_queue_received_message (transport->connection, +                                                   message); +          dbus_message_unref (message); +        } +       +      /* 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; +    } + + out: +  return; /* I think some C compilers require a statement after a label */ +} + +static void +unix_handle_watch (DBusTransport *transport, +                   DBusWatch     *watch, +                   unsigned int   flags) +{ +  DBusTransportUnix *unix_transport = (DBusTransportUnix*) transport; + +  _dbus_assert (watch == unix_transport->watch || +                watch == unix_transport->write_watch); + +  if (flags & (DBUS_WATCH_HANGUP | DBUS_WATCH_ERROR)) +    { +      _dbus_transport_disconnect (transport); +      _dbus_connection_transport_error (transport->connection, +                                        DBUS_RESULT_DISCONNECTED); +      return; +    } +   +  if (watch == unix_transport->watch && +      (flags & DBUS_WATCH_READABLE)) +    do_reading (transport); +  else if (watch == unix_transport->write_watch && +           (flags & DBUS_WATCH_WRITABLE)) +    do_writing (transport);  +} + +static void +unix_disconnect (DBusTransport *transport) +{ +  DBusTransportUnix *unix_transport = (DBusTransportUnix*) transport; + +  if (unix_transport->watch) +    { +      _dbus_connection_remove_watch (transport->connection, +                                     unix_transport->watch); +      _dbus_watch_invalidate (unix_transport->watch); +      _dbus_watch_unref (unix_transport->watch); +      unix_transport->watch = NULL; +    } +   +  close (unix_transport->fd); +  unix_transport->fd = -1; +} + +static void +unix_connection_set (DBusTransport *transport) +{ +  DBusTransportUnix *unix_transport = (DBusTransportUnix*) transport; +  DBusWatch *watch; + +  _dbus_assert (unix_transport->watch == NULL); +   +  watch = _dbus_watch_new (unix_transport->fd, +                           DBUS_WATCH_READABLE); +   +  if (watch == NULL) +    { +      _dbus_transport_disconnect (transport); +      return; +    } +   +  if (!_dbus_connection_add_watch (transport->connection, +                                   watch)) +    { +      _dbus_transport_disconnect (transport); +      return; +    } + +  unix_transport->watch = watch; +} + +static void +unix_messages_pending (DBusTransport *transport, +                       int            messages_pending) +{ +  DBusTransportUnix *unix_transport = (DBusTransportUnix*) transport; + +  if (messages_pending > 0 && +      unix_transport->write_watch == NULL) +    { +      unix_transport->write_watch = +        _dbus_watch_new (unix_transport->fd, +                         DBUS_WATCH_WRITABLE); + +      /* we can maybe add it some other time, just silently bomb */ +      if (unix_transport->write_watch == NULL) +        return; + +      if (!_dbus_connection_add_watch (transport->connection, +                                       unix_transport->write_watch)) +        { +          _dbus_watch_invalidate (unix_transport->write_watch); +          _dbus_watch_unref (unix_transport->write_watch); +          unix_transport->write_watch = NULL; +        } +    } +  else if (messages_pending == 0 && +           unix_transport->write_watch != NULL) +    { +      _dbus_connection_remove_watch (transport->connection, +                                     unix_transport->write_watch); +      _dbus_watch_invalidate (unix_transport->write_watch); +      _dbus_watch_unref (unix_transport->write_watch); +      unix_transport->write_watch = NULL; +    } +} + +static  void +unix_do_iteration (DBusTransport *transport, +                   unsigned int   flags, +                   int            timeout_milliseconds) +{ +  DBusTransportUnix *unix_transport = (DBusTransportUnix*) transport; +  fd_set read_set; +  fd_set write_set; +  dbus_bool_t do_select; +   +  do_select = FALSE; +   +  FD_ZERO (&read_set); +  if (flags & DBUS_ITERATION_DO_READING) +    { +      FD_SET (unix_transport->fd, &read_set); +      do_select = TRUE; +    } +   +  FD_ZERO (&write_set); +  if (flags & DBUS_ITERATION_DO_WRITING) +    { +      FD_SET (unix_transport->fd, &write_set); +      do_select = TRUE; +    } + +  if (do_select) +    { +      fd_set err_set; +      struct timeval timeout; +      dbus_bool_t use_timeout; + +    again: +       +      FD_ZERO (&err_set); +      FD_SET (unix_transport->fd, &err_set); +   +      if (flags & DBUS_ITERATION_BLOCK) +        { +          if (timeout_milliseconds >= 0) +            { +              timeout.tv_sec = timeout_milliseconds / 1000; +              timeout.tv_usec = (timeout_milliseconds % 1000) * 1000; +               +              /* Always use timeout if one is passed in. */ +              use_timeout = TRUE; +            } +          else +            { +              use_timeout = FALSE; /* NULL timeout to block forever */ +            } +        } +      else +        { +          /* 0 timeout to not block */ +          timeout.tv_sec = 0; +          timeout.tv_usec = 0; +          use_timeout = TRUE; +        } +       +      if (select (unix_transport->fd + 1, &read_set, &write_set, &err_set, +                  use_timeout ? &timeout : NULL) >= 0) +        { +          if (FD_ISSET (unix_transport->fd, &err_set)) +            do_io_error (transport); +          else +            { +              if (FD_ISSET (unix_transport->fd, &read_set)) +                do_reading (transport); +              if (FD_ISSET (unix_transport->fd, &write_set)) +                do_writing (transport); +            } +        } +      else if (errno == EINTR) +        goto again; +      else +        { +          _dbus_verbose ("Error from select(): %s\n", +                         _dbus_strerror (errno)); +        } +    } +} + +static DBusTransportVTable unix_vtable = { +  unix_finalize, +  unix_handle_watch, +  unix_disconnect, +  unix_connection_set, +  unix_messages_pending, +  unix_do_iteration +}; + +/** + * Creates a new transport for the given file descriptor.  The file + * descriptor must be nonblocking (use _dbus_set_fd_nonblocking() to + * make it so). This function is shared by various transports that + * boil down to a full duplex file descriptor. + * + * @param fd the file descriptor. + * @returns the new transport, or #NULL if no memory. + */ +DBusTransport* +_dbus_transport_new_for_fd (int fd) +{ +  DBusTransportUnix *unix_transport; +   +  unix_transport = dbus_new0 (DBusTransportUnix, 1); +  if (unix_transport == NULL) +    return NULL; + +  if (!_dbus_transport_init_base (&unix_transport->base, +                                  &unix_vtable)) +    { +      dbus_free (unix_transport); +      return NULL; +    } +   +  unix_transport->fd = fd; +  unix_transport->message_bytes_written = 0; +   +  /* These values should probably be tunable or something. */      +  unix_transport->max_bytes_read_per_iteration = 2048; +  unix_transport->max_bytes_written_per_iteration = 2048; +   +  return (DBusTransport*) unix_transport; +} + +/** + * Creates a new transport for the given Unix domain socket + * path. + * + * @param path the path to the domain socket. + * @param result location to store reason for failure. + * @returns a new transport, or #NULL on failure. + */ +DBusTransport* +_dbus_transport_new_for_domain_socket (const char     *path, +                                       DBusResultCode *result) +{ +  int fd; +  DBusTransport *transport; +  struct sockaddr_un addr; +   +  transport = NULL; +   +  fd = socket (AF_LOCAL, SOCK_STREAM, 0); + +  if (fd < 0) +    { +      dbus_set_result (result, +                       _dbus_result_from_errno (errno)); +       +      _dbus_verbose ("Failed to create socket: %s\n", +                     _dbus_strerror (errno));  +       +      goto out; +    } + +  _DBUS_ZERO (addr); +  addr.sun_family = AF_LOCAL; +  strncpy (addr.sun_path, path, _DBUS_MAX_SUN_PATH_LENGTH); +  addr.sun_path[_DBUS_MAX_SUN_PATH_LENGTH] = '\0'; +   +  if (connect (fd, (struct sockaddr*) &addr, sizeof (addr)) < 0) +    {       +      dbus_set_result (result, +                       _dbus_result_from_errno (errno)); + +      _dbus_verbose ("Failed to connect to socket %s: %s\n", +                     path, _dbus_strerror (errno)); + +      close (fd); +      fd = -1; +       +      goto out; +    } + +  if (!_dbus_set_fd_nonblocking (fd, result)) +    { +      close (fd); +      fd = -1; + +      goto out; +    } +   +  transport = _dbus_transport_new_for_fd (fd); +  if (transport == NULL) +    { +      dbus_set_result (result, DBUS_RESULT_NO_MEMORY); +      close (fd); +      fd = -1; +      goto out; +    } +   + out:   +  return transport; +} + + +/** @} */ + diff --git a/dbus/dbus-transport-unix.h b/dbus/dbus-transport-unix.h new file mode 100644 index 00000000..c483f05e --- /dev/null +++ b/dbus/dbus-transport-unix.h @@ -0,0 +1,37 @@ +/* -*- mode: C; c-file-style: "gnu" -*- */ +/* dbus-transport-unix.h UNIX socket subclasses of DBusTransport + * + * 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 + * + */ +#ifndef DBUS_TRANSPORT_UNIX_H +#define DBUS_TRANSPORT_UNIX_H + +#include <dbus/dbus-transport.h> + +DBUS_BEGIN_DECLS; + +DBusTransport* _dbus_transport_new_for_fd            (int             fd); +DBusTransport* _dbus_transport_new_for_domain_socket (const char     *path, +                                                      DBusResultCode *result); + + +DBUS_END_DECLS; + +#endif /* DBUS_TRANSPORT_UNIX_H */ diff --git a/dbus/dbus-transport.c b/dbus/dbus-transport.c new file mode 100644 index 00000000..85498872 --- /dev/null +++ b/dbus/dbus-transport.c @@ -0,0 +1,288 @@ +/* -*- mode: C; c-file-style: "gnu" -*- */ +/* dbus-transport.c DBusTransport object (internal to D-BUS implementation) + * + * 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-transport-protected.h" +#include "dbus-transport-unix.h" +#include "dbus-connection-internal.h" + +/** + * @defgroup DBusTransport DBusTransport object + * @ingroup  DBusInternals + * @brief "Backend" for a DBusConnection. + * + * Types and functions related to DBusTransport.  A transport is an + * abstraction that can send and receive data via various kinds of + * network connections or other IPC mechanisms. + *  + * @{ + */ + +/** + * @typedef DBusTransport + * + * Opaque object representing a way message stream. + * DBusTransport abstracts various kinds of actual + * transport mechanism, such as different network protocols, + * or encryption schemes. + */ + +/** + * Initializes the base class members of DBusTransport. + * Chained up to by subclasses in their constructor. + * + * @param transport the transport being created. + * @param vtable the subclass vtable. + * @returns #TRUE on success. + */ +dbus_bool_t +_dbus_transport_init_base (DBusTransport             *transport, +                           const DBusTransportVTable *vtable) +{ +  DBusMessageLoader *loader; + +  loader = _dbus_message_loader_new (); +  if (loader == NULL) +    return FALSE; +   +  transport->refcount = 1; +  transport->vtable = vtable; +  transport->loader = loader; +   +  return TRUE; +} + +/** + * Finalizes base class members of DBusTransport. + * Chained up to from subclass finalizers. + * + * @param transport the transport. + */ +void +_dbus_transport_finalize_base (DBusTransport *transport) +{ +  if (!transport->disconnected) +    _dbus_transport_disconnect (transport); + +  _dbus_message_loader_unref (transport->loader); +} + +/** + * Opens a new transport for the given address. + * + * @todo right now the address is just a Unix domain socket path. + *  + * @param address the address. + * @param result location to store reason for failure. + * @returns new transport of #NULL on failure. + */ +DBusTransport* +_dbus_transport_open (const char     *address, +                      DBusResultCode *result) +{ +  DBusTransport *transport; +   +  /* FIXME parse the address - whatever format +   * we decide addresses are in - and find the +   * appropriate transport. +   */ + +  /* Pretend it's just a unix domain socket name for now */ +  transport = _dbus_transport_new_for_domain_socket (address, result); +   +  return transport; +} + +/** + * Increments the reference count for the transport. + * + * @param transport the transport. + */ +void +_dbus_transport_ref (DBusTransport *transport) +{ +  transport->refcount += 1; +} + +/** + * Decrements the reference count for the transport. + * Disconnects and finalizes the transport if + * the reference count reaches zero. + * + * @param transport the transport. + */ +void +_dbus_transport_unref (DBusTransport *transport) +{ +  _dbus_assert (transport != NULL); +  _dbus_assert (transport->refcount > 0); + +  transport->refcount -= 1; +  if (transport->refcount == 0) +    { +      _dbus_assert (transport->vtable->finalize != NULL); +       +      (* transport->vtable->finalize) (transport); +    } +} + +/** + * Closes our end of the connection to a remote application. Further + * attempts to use this transport will fail. Only the first call to + * _dbus_transport_disconnect() will have an effect. + * + * @param transport the transport. + *  + */ +void +_dbus_transport_disconnect (DBusTransport *transport) +{ +  _dbus_assert (transport->vtable->disconnect != NULL); + +  if (transport->disconnected) +    return; +   +  (* transport->vtable->disconnect) (transport); + +  transport->disconnected = TRUE; +} + +/** + * Returns #TRUE if the transport has not been disconnected. + * Disconnection can result from _dbus_transport_disconnect() + * or because the server drops its end of the connection. + * + * @param transport the transport. + */ +dbus_bool_t +_dbus_transport_get_is_connected (DBusTransport *transport) +{ +  return !transport->disconnected; +} + +/** + * Handles a watch by reading data, writing data, or disconnecting + * the transport, as appropriate for the given condition. + * + * @param transport the transport. + * @param watch the watch. + * @param condition the current state of the watched file descriptor. + */ +void +_dbus_transport_handle_watch (DBusTransport           *transport, +                              DBusWatch               *watch, +                              unsigned int             condition) +{ +  _dbus_assert (transport->vtable->handle_watch != NULL); + +  if (transport->disconnected) +    { +      _dbus_connection_transport_error (transport->connection, +                                        DBUS_RESULT_DISCONNECTED); +      return; +    } + +  _dbus_watch_sanitize_condition (watch, &condition); +   +  (* transport->vtable->handle_watch) (transport, watch, condition); +} + +/** + * Sets the connection using this transport. Allows the transport + * to add watches to the connection, queue incoming messages, + * and pull outgoing messages. + * + * @param transport the transport. + * @param connection the connection. + */ +void +_dbus_transport_set_connection (DBusTransport  *transport, +                                DBusConnection *connection) +{ +  _dbus_assert (transport->vtable->connection_set != NULL); +  _dbus_assert (transport->connection == NULL); +   +  transport->connection = connection; + +  (* transport->vtable->connection_set) (transport); +} + +/** + * Notifies the transport when the outgoing message queue goes from + * empty to non-empty or vice versa. Typically causes the transport to + * add or remove its DBUS_WATCH_WRITABLE watch. + * + * @param transport the transport. + * @param queue_length the length of the outgoing message queue. + * + */ +void +_dbus_transport_messages_pending (DBusTransport  *transport, +                                  int             queue_length) +{ +  _dbus_assert (transport->vtable->messages_pending != NULL); + +  if (transport->disconnected) +    { +      _dbus_connection_transport_error (transport->connection, +                                        DBUS_RESULT_DISCONNECTED); +      return; +    } +   +  (* transport->vtable->messages_pending) (transport, +                                           queue_length); +} + +/** + * Performs a single poll()/select() on the transport's file + * descriptors and then reads/writes data as appropriate, + * queueing incoming messages and sending outgoing messages. + * This is the backend for _dbus_connection_do_iteration(). + * See _dbus_connection_do_iteration() for full details. + * + * @param transport the transport. + * @param flags indicates whether to read or write, and whether to block. + * @param timeout_milliseconds if blocking, timeout or -1 for no timeout. + */ +void +_dbus_transport_do_iteration (DBusTransport  *transport, +                              unsigned int    flags, +                              int             timeout_milliseconds) +{ +  _dbus_assert (transport->vtable->do_iteration != NULL); + +  if ((flags & (DBUS_ITERATION_DO_WRITING | +                DBUS_ITERATION_DO_READING)) == 0) +    return; /* Nothing to do */ + +  if (transport->disconnected) +    { +      _dbus_connection_transport_error (transport->connection, +                                        DBUS_RESULT_DISCONNECTED); +      return; +    } +   +  (* transport->vtable->do_iteration) (transport, flags, +                                       timeout_milliseconds); +} + +/** @} */ diff --git a/dbus/dbus-transport.h b/dbus/dbus-transport.h new file mode 100644 index 00000000..84c1f8da --- /dev/null +++ b/dbus/dbus-transport.h @@ -0,0 +1,53 @@ +/* -*- mode: C; c-file-style: "gnu" -*- */ +/* dbus-transport.h DBusTransport object (internal to D-BUS implementation) + * + * 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 + * + */ +#ifndef DBUS_TRANSPORT_H +#define DBUS_TRANSPORT_H + +#include <dbus/dbus-internals.h> +#include <dbus/dbus-connection.h> + +DBUS_BEGIN_DECLS; + +typedef struct DBusTransport DBusTransport; + +DBusTransport* _dbus_transport_open             (const char     *address, +                                                 DBusResultCode *result); +void           _dbus_transport_ref              (DBusTransport  *transport); +void           _dbus_transport_unref            (DBusTransport  *transport); +void           _dbus_transport_disconnect       (DBusTransport  *transport); +dbus_bool_t    _dbus_transport_get_is_connected (DBusTransport  *transport); +void           _dbus_transport_handle_watch     (DBusTransport  *transport, +                                                 DBusWatch      *watch, +                                                 unsigned int    condition); +void           _dbus_transport_set_connection   (DBusTransport  *transport, +                                                 DBusConnection *connection); +void           _dbus_transport_messages_pending (DBusTransport  *transport, +                                                 int             queue_length); +void           _dbus_transport_do_iteration     (DBusTransport  *transport, +                                                 unsigned int    flags, +                                                 int             timeout_milliseconds); + + +DBUS_END_DECLS; + +#endif /* DBUS_TRANSPORT_H */ diff --git a/dbus/dbus-types.h b/dbus/dbus-types.h index d12b4100..e55e0053 100644 --- a/dbus/dbus-types.h +++ b/dbus/dbus-types.h @@ -28,8 +28,39 @@  #define DBUS_TYPES_H  typedef unsigned int dbus_bool_t; -  typedef unsigned int dbus_uint32_t;  typedef int          dbus_int32_t; + +/* Normally docs are in .c files, but there isn't a .c file for this. */ +/** + * @defgroup DBusTypes Basic types + * @ingroup  DBus + * @brief dbus_bool_t, dbus_int32_t, etc. + * + * Typedefs for common primitive types. + * + * @{ + */ + +/** + * @typedef dbus_bool_t + * + * A boolean, valid values are #TRUE and #FALSE. + */ + +/** + * @typedef dbus_uint32_t + * + * A 32-bit unsigned integer on all platforms. + */ + +/** + * @typedef dbus_int32_t + * + * A 32-bit signed integer on all platforms. + */ + +/** @} */ +  #endif /* DBUS_TYPES_H */ diff --git a/dbus/dbus-watch.c b/dbus/dbus-watch.c new file mode 100644 index 00000000..8f3c16bc --- /dev/null +++ b/dbus/dbus-watch.c @@ -0,0 +1,381 @@ +/* -*- mode: C; c-file-style: "gnu" -*- */ +/* dbus-watch.c DBusWatch implementation + * + * 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-watch.h" +#include "dbus-list.h" + +/** + * @defgroup DBusWatchInternals DBusWatch implementation details + * @ingroup  DBusInternals + * @brief implementation details for DBusWatch + *  + * @{ + */ + +struct DBusWatch +{ +  int refcount;                        /**< Reference count */ +  int fd;                              /**< File descriptor. */ +  unsigned int flags;                  /**< Conditions to watch. */ +  void *data;                          /**< Application data. */ +  DBusFreeFunction free_data_function; /**< Free the application data. */ +}; + +/** + * Creates a new DBusWatch. Normally used by a DBusTransport + * implementation. + * @param fd the file descriptor to be watched. + * @param flags the conditions to watch for on the descriptor. + * @returns the new DBusWatch object. + */ +DBusWatch* +_dbus_watch_new (int          fd, +                 unsigned int flags) +{ +  DBusWatch *watch; + +#define VALID_WATCH_FLAGS (DBUS_WATCH_WRITABLE | DBUS_WATCH_READABLE) +   +  _dbus_assert ((flags & VALID_WATCH_FLAGS) == flags); +   +  watch = dbus_new0 (DBusWatch, 1); +  watch->refcount = 1; +  watch->fd = fd; +  watch->flags = flags; + +  return watch; +} + +/** + * Increments the reference count of a DBusWatch object. + * + * @param watch the watch object. + */ +void +_dbus_watch_ref (DBusWatch *watch) +{ +  watch->refcount += 1; +} + +/** + * Decrements the reference count of a DBusWatch object + * and finalizes the object if the count reaches zero. + * + * @param watch the watch object. + */ +void +_dbus_watch_unref (DBusWatch *watch) +{ +  _dbus_assert (watch != NULL); +  _dbus_assert (watch->refcount > 0); + +  watch->refcount -= 1; +  if (watch->refcount == 0) +    { +      dbus_watch_set_data (watch, NULL, NULL); /* call free_data_function */ +      dbus_free (watch); +    } +} + +/** + * Clears the file descriptor from a now-invalid watch object so that + * no one tries to use it.  This is because a watch may stay alive due + * to reference counts after the file descriptor is closed. + * Invalidation makes it easier to catch bugs. It also + * keeps people from doing dorky things like assuming file descriptors + * are unique (never recycled). + * + * @param watch the watch object. + */ +void +_dbus_watch_invalidate (DBusWatch *watch) +{ +  watch->fd = -1; +  watch->flags = 0; +} + +/** + * Sanitizes the given condition so that it only contains + * flags that the DBusWatch requested. e.g. if the + * watch is a DBUS_WATCH_READABLE watch then + * DBUS_WATCH_WRITABLE will be stripped from the condition. + * + * @param watch the watch object. + * @param condition address of the condition to sanitize. + */ +void +_dbus_watch_sanitize_condition (DBusWatch    *watch, +                                unsigned int *condition) +{ +  if (!(watch->flags & DBUS_WATCH_READABLE)) +    *condition &= ~DBUS_WATCH_READABLE; +  if (!(watch->flags & DBUS_WATCH_WRITABLE)) +    *condition &= ~DBUS_WATCH_WRITABLE; +} + + +/** + * @typedef DBusWatchList + * + * Opaque data type representing a list of watches + * and a set of DBusAddWatchFunction/DBusRemoveWatchFunction. + * Automatically handles removing/re-adding watches + * when the DBusAddWatchFunction is updated or changed. + * Holds a reference count to each watch. + * + * Used in the implementation of both DBusServer and + * DBusClient. + * + */ + +/** + * DBusWatchList implementation details. All fields + * are private. + * + */ +struct DBusWatchList +{ +  DBusList *watches;           /**< Watch objects. */ + +  DBusAddWatchFunction add_watch_function;    /**< Callback for adding a watch. */ +  DBusAddWatchFunction remove_watch_function; /**< Callback for removing a watch. */ +  void *watch_data;                           /**< Data for watch callbacks */ +  DBusFreeFunction watch_free_data_function;  /**< Free function for watch callback data */ +}; + +/** + * Creates a new watch list. Returns @NULL if insufficient + * memory exists. + * + * @returns the new watch list, or #NULL on failure. + */ +DBusWatchList* +_dbus_watch_list_new (void) +{ +  DBusWatchList *watch_list; + +  watch_list = dbus_new0 (DBusWatchList, 1); +  if (watch_list == NULL) +    return NULL; + +  return watch_list; +} + +/** + * Frees a DBusWatchList. + * + * @param watch_list the watch list. + */ +void +_dbus_watch_list_free (DBusWatchList *watch_list) +{ +  /* free watch_data as a side effect */ +  _dbus_watch_list_set_functions (watch_list, +                                  NULL, NULL, NULL, NULL); +   +  _dbus_list_foreach (&watch_list->watches, +                      (DBusForeachFunction) _dbus_watch_unref, +                      NULL); +  _dbus_list_clear (&watch_list->watches); + +  dbus_free (watch_list); +} + +/** + * Sets the watch functions. This function is the "backend" + * for dbus_connection_set_watch_functions() and + * dbus_server_set_watch_functions(). + * + * @param watch_list the watch list. + * @param add_function the add watch function. + * @param remove_function the remove watch function. + * @param data the data for those functions. + * @param free_data_function the function to free the data. + * + */ +void +_dbus_watch_list_set_functions (DBusWatchList           *watch_list, +                                DBusAddWatchFunction     add_function, +                                DBusRemoveWatchFunction  remove_function, +                                void                    *data, +                                DBusFreeFunction         free_data_function) +{ +  /* Remove all current watches from previous watch handlers */ + +  if (watch_list->remove_watch_function != NULL) +    { +      _dbus_list_foreach (&watch_list->watches, +                          (DBusForeachFunction) watch_list->remove_watch_function, +                          watch_list->watch_data); +    } + +  if (watch_list->watch_free_data_function != NULL) +    (* watch_list->watch_free_data_function) (watch_list->watch_data); +   +  watch_list->add_watch_function = add_function; +  watch_list->remove_watch_function = remove_function; +  watch_list->watch_data = data; +  watch_list->watch_free_data_function = free_data_function; + +  /* Re-add all pending watches */ +  if (watch_list->add_watch_function != NULL) +    { +      _dbus_list_foreach (&watch_list->watches, +                          (DBusForeachFunction) watch_list->add_watch_function, +                          watch_list->watch_data); +    } +} + +/** + * Adds a new watch to the watch list, invoking the + * application DBusAddWatchFunction if appropriate. + * + * @param watch_list the watch list. + * @param watch the watch to add. + * @returns #TRUE on success, #FALSE if no memory. + */ +dbus_bool_t +_dbus_watch_list_add_watch (DBusWatchList *watch_list, +                            DBusWatch     *watch) +{ +  if (!_dbus_list_append (&watch_list->watches, watch)) +    return FALSE; +   +  _dbus_watch_ref (watch); + +  if (watch_list->add_watch_function != NULL) +    (* watch_list->add_watch_function) (watch, +                                        watch_list->watch_data); +   +  return TRUE; +} + +/** + * Removes a watch from the watch list, invoking the + * application's DBusRemoveWatchFunction if appropriate. + * + * @param watch_list the watch list. + * @param watch the watch to remove. + */ +void +_dbus_watch_list_remove_watch  (DBusWatchList *watch_list, +                                DBusWatch     *watch) +{ +  if (!_dbus_list_remove (&watch_list->watches, watch)) +    _dbus_assert_not_reached ("Nonexistent watch was removed"); + +  if (watch_list->remove_watch_function != NULL) +    (* watch_list->remove_watch_function) (watch, +                                           watch_list->watch_data); +   +  _dbus_watch_unref (watch); +} + +/** @} */ + +/** + * @defgroup DBusWatch DBusWatch + * @ingroup  DBus + * @brief Object representing an file descriptor to be watched. + * + * Types and functions related to DBusWatch. A watch represents + * a file descriptor that the main loop needs to monitor, + * as in Qt's QSocketNotifier or GLib's g_io_add_watch(). + *  + * @{ + */ + +/** + * @typedef DBusWatch + * + * Opaque object representing a file descriptor + * to be watched for changes in readability, + * writability, or hangup. + */ + +/** + * Gets the file descriptor that should be watched. + * + * @param watch the DBusWatch object. + * @returns the file descriptor to watch. + */ +int +dbus_watch_get_fd (DBusWatch *watch) +{ +  return watch->fd; +} + +/** + * Gets flags from DBusWatchFlags indicating + * what conditions should be monitored on the + * file descriptor. + * + * @param watch the DBusWatch object. + * @returns the conditions to watch. + */ +unsigned int +dbus_watch_get_flags (DBusWatch *watch) +{ +  _dbus_assert ((watch->flags & VALID_WATCH_FLAGS) == watch->flags); + +  return watch->flags; +} + +/** + * Gets data previously set with dbus_watch_set_data() + * or #NULL if none. + * + * @param watch the DBusWatch object. + * @returns previously-set data. + */ +void* +dbus_watch_get_data (DBusWatch *watch) +{ +  return watch->data; +} + +/** + * Sets data which can be retrieved with dbus_watch_get_data(). + * Intended for use by the DBusAddWatchFunction and + * DBusRemoveWatchFunction to store their own data.  For example with + * Qt you might store the QSocketNotifier for this watch and with GLib + * you might store a GSource. + * + * @param watch the DBusWatch object. + * @param data the data. + * @param free_data_function function to be called to free the data. + */ +void +dbus_watch_set_data (DBusWatch        *watch, +                     void             *data, +                     DBusFreeFunction  free_data_function) +{ +  if (watch->free_data_function != NULL) +    (* watch->free_data_function) (watch->data); +   +  watch->data = data; +  watch->free_data_function = free_data_function; +} + +/** @} */ diff --git a/dbus/dbus-watch.h b/dbus/dbus-watch.h new file mode 100644 index 00000000..869605ae --- /dev/null +++ b/dbus/dbus-watch.h @@ -0,0 +1,60 @@ +/* -*- mode: C; c-file-style: "gnu" -*- */ +/* dbus-watch.h DBusWatch internal interfaces + * + * 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 + * + */ +#ifndef DBUS_WATCH_H +#define DBUS_WATCH_H + +#include <dbus/dbus-internals.h> +#include <dbus/dbus-connection.h> + +DBUS_BEGIN_DECLS; + +/* Public methods on DBusWatch are in dbus-connection.h */ + +typedef struct DBusWatchList DBusWatchList; + +DBusWatch* _dbus_watch_new        (int           fd, +                                   unsigned int  flags); +void       _dbus_watch_ref        (DBusWatch    *watch); +void       _dbus_watch_unref      (DBusWatch    *watch); +void       _dbus_watch_invalidate (DBusWatch    *watch); + +void       _dbus_watch_sanitize_condition (DBusWatch *watch, +                                           unsigned int *condition); + +DBusWatchList* _dbus_watch_list_new           (void); +void           _dbus_watch_list_free          (DBusWatchList           *watch_list); +void           _dbus_watch_list_set_functions (DBusWatchList           *watch_list, +                                               DBusAddWatchFunction     add_function, +                                               DBusRemoveWatchFunction  remove_function, +                                               void                    *data, +                                               DBusFreeFunction         free_data_function); +dbus_bool_t    _dbus_watch_list_add_watch     (DBusWatchList           *watch_list, +                                               DBusWatch               *watch); +void           _dbus_watch_list_remove_watch  (DBusWatchList           *watch_list, +                                               DBusWatch               *watch); + + + +DBUS_END_DECLS; + +#endif /* DBUS_WATCH_H */ diff --git a/dbus/dbus.h b/dbus/dbus.h index c03eb311..8bebaf59 100644 --- a/dbus/dbus.h +++ b/dbus/dbus.h @@ -26,8 +26,11 @@  #define DBUS_INSIDE_DBUS_H 1 +#include <dbus/dbus-connection.h> +#include <dbus/dbus-errors.h>  #include <dbus/dbus-macros.h>  #include <dbus/dbus-message.h> +#include <dbus/dbus-server.h>  #include <dbus/dbus-types.h>  #undef DBUS_INSIDE_DBUS_H @@ -42,4 +45,4 @@  /** @} */ -#endif /* DBUS_H +#endif /* DBUS_H */ diff --git a/doc/.cvsignore b/doc/.cvsignore new file mode 100644 index 00000000..444a9eba --- /dev/null +++ b/doc/.cvsignore @@ -0,0 +1,8 @@ +.deps +.libs +Makefile +Makefile.in +*.lo +*.la +*.o +api
\ No newline at end of file diff --git a/test/.cvsignore b/test/.cvsignore new file mode 100644 index 00000000..53012e2b --- /dev/null +++ b/test/.cvsignore @@ -0,0 +1,9 @@ +.deps +.libs +Makefile +Makefile.in +*.lo +*.la +*.o +echo-server +echo-client diff --git a/test/Makefile.am b/test/Makefile.am index 6e42b47b..8f7d8e1d 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -1,4 +1,23 @@  if DBUS_BUILD_TESTS +INCLUDES=$(DBUS_TEST_CFLAGS) + +noinst_PROGRAMS= echo-client echo-server + +echo_client_SOURCES=				\ +	echo-client.c				\ +	watch.c					\ +	watch.h + +echo_server_SOURCES=				\ +	echo-server.c				\ +	watch.c					\ +	watch.h + +TEST_LIBS=$(DBUS_TEST_LIBS) $(top_builddir)/dbus/libdbus-convenience.la $(top_builddir)/dbus/libdbus-1.la + +echo_client_LDADD=$(TEST_LIBS) +echo_server_LDADD=$(TEST_LIBS) +  endif diff --git a/test/echo-client.c b/test/echo-client.c new file mode 100644 index 00000000..5c6ee425 --- /dev/null +++ b/test/echo-client.c @@ -0,0 +1,41 @@ +#include <dbus/dbus.h> +#include <stdio.h> +#include "watch.h" + +int +main (int    argc, +      char **argv) +{ +  DBusConnection *connection; +  DBusResultCode result; +  DBusMessage *message; +   +  if (argc < 2) +    { +      fprintf (stderr, "Give the server address as an argument\n"); +      return 1; +    } +   +  connection = dbus_connection_open (argv[1], &result); +  if (connection == NULL) +    { +      fprintf (stderr, "Failed to open connection to %s: %s\n", +               argv[1], dbus_result_to_string (result)); +      return 1; +    } + +  setup_connection (connection); + +  /* Send a message to get things going */ +  message = dbus_message_new (); +  dbus_connection_send_message (connection, +                                message, +                                NULL); +  dbus_message_unref (message); +   +  do_mainloop (); + +  dbus_connection_unref (connection); +   +  return 0; +} diff --git a/test/echo-server.c b/test/echo-server.c new file mode 100644 index 00000000..99f97ffd --- /dev/null +++ b/test/echo-server.c @@ -0,0 +1,48 @@ +#include <dbus/dbus.h> +#include <stdio.h> +#include "watch.h" + +static void +new_connection_callback (DBusServer     *server, +                         DBusConnection *new_connection, +                         void           *data) +{ +  printf ("Got new connection\n"); + +  setup_connection (new_connection); +} + +int +main (int    argc, +      char **argv) +{ +  DBusServer *server; +  DBusResultCode result; + +  if (argc < 2) +    { +      fprintf (stderr, "Give the server address as an argument\n"); +      return 1; +    } + +  server = dbus_server_listen (argv[1], &result); +  if (server == NULL) +    { +      fprintf (stderr, "Failed to start server on %s: %s\n", +               argv[1], dbus_result_to_string (result)); +      return 1; +    } + +  setup_server (server); + +  dbus_server_set_new_connection_function (server, +                                           new_connection_callback, +                                           NULL, NULL); +   +  do_mainloop (); + +  dbus_server_disconnect (server); +  dbus_server_unref (server); +   +  return 0; +} diff --git a/test/watch.c b/test/watch.c new file mode 100644 index 00000000..df26855c --- /dev/null +++ b/test/watch.c @@ -0,0 +1,268 @@ +#include "watch.h" +#include <stdio.h> + +#define DBUS_COMPILATION /* cheat and use DBusList */ +#include <dbus/dbus-list.h> +#undef DBUS_COMPILATION + +/* Cheesy main loop used in test programs.  Any real app would use the + * GLib or Qt or other non-sucky main loops. + */  + +#undef	MAX +#define MAX(a, b)  (((a) > (b)) ? (a) : (b)) + +static DBusList *watches = NULL; +static dbus_bool_t exited = FALSE; +static DBusList *connections = NULL; + +typedef enum +{ +  WATCH_CONNECTION, +  WATCH_SERVER +} WatchType; + +typedef struct +{ +  WatchType type; +  void *data; +} WatchData; + +static void +add_connection_watch (DBusWatch      *watch, +                      DBusConnection *connection) +{ +  WatchData *wd; + +  wd = dbus_new0 (WatchData, 1); +  wd->type = WATCH_CONNECTION; +  wd->data = connection; +   +  _dbus_list_append (&watches, watch); +  dbus_watch_set_data (watch, wd, dbus_free); +} + +static void +remove_connection_watch (DBusWatch      *watch, +                         DBusConnection *connection) +{ +  _dbus_list_remove (&watches, watch); +  dbus_watch_set_data (watch, NULL, NULL); +} + +static void +add_server_watch (DBusWatch      *watch, +                  DBusServer     *server) +{ +  WatchData *wd; +   +  wd = dbus_new0 (WatchData, 1); +  wd->type = WATCH_SERVER; +  wd->data = server; +   +  _dbus_list_append (&watches, watch); + +  dbus_watch_set_data (watch, wd, dbus_free); +} + +static void +remove_server_watch (DBusWatch      *watch, +                     DBusServer     *server) +{ +  _dbus_list_remove (&watches, watch); +  dbus_watch_set_data (watch, NULL, NULL); +} + +static int count = 0; + +static void +check_messages (void) +{ +  DBusList *link; +   +  link = _dbus_list_get_first_link (&connections); +  while (link != NULL) +    { +      DBusList *next = _dbus_list_get_next_link (&connections, link); +      DBusConnection *connection = link->data; +      DBusMessage *message; +       +      while ((message = dbus_connection_pop_message (connection))) +        { +          DBusMessage *reply; + +          printf ("Received message %d, sending reply\n", count); +           +          reply = dbus_message_new (); +          dbus_connection_send_message (connection, +                                        reply, +                                        NULL); +          dbus_message_unref (reply); + +          dbus_message_unref (message); + +          count += 1; +          if (count > 100) +            { +              printf ("Saw %d messages, exiting\n", count); +              quit_mainloop (); +            } +        } +       +      link = next; +    } +} + +void +do_mainloop (void) +{ +  /* Of course with any real app you'd use GMainLoop or +   * QSocketNotifier and not have to see all this crap. +   */ +   +  while (!exited && watches != NULL) +    { +      fd_set read_set; +      fd_set write_set; +      fd_set err_set; +      int max_fd; +      DBusList *link; + +      check_messages (); +       +      FD_ZERO (&read_set); +      FD_ZERO (&write_set); +      FD_ZERO (&err_set); + +      max_fd = -1; + +      link = _dbus_list_get_first_link (&watches); +      while (link != NULL) +        { +          DBusList *next = _dbus_list_get_next_link (&watches, link); +          int fd; +          DBusWatch *watch; +          unsigned int flags; +           +          watch = link->data; +           +          fd = dbus_watch_get_fd (watch); +          flags = dbus_watch_get_flags (watch); +           +          max_fd = MAX (max_fd, fd); +           +          if (flags & DBUS_WATCH_READABLE) +            FD_SET (fd, &read_set); + +          if (flags & DBUS_WATCH_WRITABLE) +            FD_SET (fd, &write_set); + +          FD_SET (fd, &err_set); +           +          link = next; +        } + +      select (max_fd + 1, &read_set, &write_set, &err_set, NULL); + +      link = _dbus_list_get_first_link (&watches); +      while (link != NULL) +        { +          DBusList *next = _dbus_list_get_next_link (&watches, link); +          int fd; +          DBusWatch *watch; +          unsigned int flags; +          unsigned int condition; +           +          watch = link->data; +           +          fd = dbus_watch_get_fd (watch); +          flags = dbus_watch_get_flags (watch); + +          condition = 0; +           +          if ((flags & DBUS_WATCH_READABLE) && +              FD_ISSET (fd, &read_set)) +            condition |= DBUS_WATCH_READABLE; + +          if ((flags & DBUS_WATCH_WRITABLE) && +              FD_ISSET (fd, &write_set)) +            condition |= DBUS_WATCH_WRITABLE; + +          if (FD_ISSET (fd, &err_set)) +            condition |= DBUS_WATCH_ERROR; + +          if (condition != 0) +            { +              WatchData *wd; + +              wd = dbus_watch_get_data (watch); + +              if (wd->type == WATCH_CONNECTION) +                { +                  DBusConnection *connection = wd->data; + +                  dbus_connection_handle_watch (connection, +                                                watch, +                                                condition); +                } +              else if (wd->type == WATCH_SERVER) +                { +                  DBusServer *server = wd->data; +                   +                  dbus_server_handle_watch (server, +                                            watch, +                                            condition); +                } +            } +           +          link = next; +        } +    } +} + +void +quit_mainloop (void) +{ +  exited = TRUE; +} + +static void +error_handler (DBusConnection *connection, +               DBusResultCode  error_code, +               void           *data) +{ +  fprintf (stderr, +           "Error on connection: %s\n", +           dbus_result_to_string (error_code)); + +  _dbus_list_remove (&connections, connection); +  dbus_connection_unref (connection); +  quit_mainloop (); +} + +void +setup_connection (DBusConnection *connection) +{ +  dbus_connection_set_watch_functions (connection, +                                       (DBusAddWatchFunction) add_connection_watch, +                                       (DBusRemoveWatchFunction) remove_connection_watch, +                                       connection, +                                       NULL); + +  dbus_connection_set_error_function (connection, +                                      error_handler, +                                      NULL, NULL); + +  dbus_connection_ref (connection); +  _dbus_list_append (&connections, connection); +} + +void +setup_server (DBusServer *server) +{ +  dbus_server_set_watch_functions (server, +                                   (DBusAddWatchFunction) add_server_watch, +                                   (DBusRemoveWatchFunction) remove_server_watch, +                                   server, +                                   NULL); +} diff --git a/test/watch.h b/test/watch.h new file mode 100644 index 00000000..a9ad0834 --- /dev/null +++ b/test/watch.h @@ -0,0 +1,15 @@ +/* Cheesy main loop thingy used by the test programs */ + +#ifndef WATCH_H +#define WATCH_H + +#include <dbus/dbus.h> + +void do_mainloop (void); + +void quit_mainloop (void); + +void setup_connection (DBusConnection *connection); +void setup_server     (DBusServer *server); + +#endif  | 
