summaryrefslogtreecommitdiffstats
path: root/dbus
diff options
context:
space:
mode:
authorHavoc Pennington <hp@redhat.com>2002-11-25 05:13:09 +0000
committerHavoc Pennington <hp@redhat.com>2002-11-25 05:13:09 +0000
commit041b0767b284034aee09e9a0de2a3844b8cc546a (patch)
tree8ef9cce16d743350971696ff6333ce43686c7ac0 /dbus
parent576cdb6e0b1274e9fa5276e01337aef330dd4e8c (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.
Diffstat (limited to 'dbus')
-rw-r--r--dbus/.cvsignore7
-rw-r--r--dbus/Makefile.am24
-rw-r--r--dbus/dbus-connection-internal.h64
-rw-r--r--dbus/dbus-connection.c623
-rw-r--r--dbus/dbus-connection.h100
-rw-r--r--dbus/dbus-errors.c109
-rw-r--r--dbus/dbus-errors.h62
-rw-r--r--dbus/dbus-internals.c196
-rw-r--r--dbus/dbus-internals.h23
-rw-r--r--dbus/dbus-list.c28
-rw-r--r--dbus/dbus-list.h5
-rw-r--r--dbus/dbus-message-internal.h56
-rw-r--r--dbus/dbus-message.c325
-rw-r--r--dbus/dbus-message.h1
-rw-r--r--dbus/dbus-server-protected.h79
-rw-r--r--dbus/dbus-server-unix.c303
-rw-r--r--dbus/dbus-server-unix.h37
-rw-r--r--dbus/dbus-server.c299
-rw-r--r--dbus/dbus-server.h66
-rw-r--r--dbus/dbus-transport-protected.h88
-rw-r--r--dbus/dbus-transport-unix.c581
-rw-r--r--dbus/dbus-transport-unix.h37
-rw-r--r--dbus/dbus-transport.c288
-rw-r--r--dbus/dbus-transport.h53
-rw-r--r--dbus/dbus-types.h33
-rw-r--r--dbus/dbus-watch.c381
-rw-r--r--dbus/dbus-watch.h60
-rw-r--r--dbus/dbus.h5
28 files changed, 3918 insertions, 15 deletions
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 */