2002-11-24 Havoc Pennington <hp@pobox.com>
authorHavoc Pennington <hp@redhat.com>
Mon, 25 Nov 2002 05:13:09 +0000 (05:13 +0000)
committerHavoc Pennington <hp@redhat.com>
Mon, 25 Nov 2002 05:13:09 +0000 (05:13 +0000)
        * test/echo-client.c, test/echo-server.c: cheesy test
clients.

* configure.in (AC_CHECK_FUNCS): check for writev

* dbus/dbus-message.c (_dbus_message_get_network_data): new
function

* dbus/dbus-list.c (_dbus_list_foreach): new function

* dbus/dbus-internals.c (_dbus_verbose): new function

* dbus/dbus-server.c, dbus/dbus-server.h: public object
representing a server that listens for connections.

* dbus/.cvsignore: create

* dbus/dbus-errors.h, dbus/dbus-errors.c:
public API for reporting errors

* dbus/dbus-connection.h, dbus/dbus-connection.c:
public object representing a connection that
sends/receives messages. (Same object used for
both client and server.)

* dbus/dbus-transport.h, dbus/dbus-transport.c:
Basic abstraction for different kinds of stream
that we might read/write messages from.

39 files changed:
.cvsignore [new file with mode: 0644]
ChangeLog
bus/.cvsignore [new file with mode: 0644]
configure.in
dbus/.cvsignore [new file with mode: 0644]
dbus/Makefile.am
dbus/dbus-connection-internal.h [new file with mode: 0644]
dbus/dbus-connection.c [new file with mode: 0644]
dbus/dbus-connection.h [new file with mode: 0644]
dbus/dbus-errors.c [new file with mode: 0644]
dbus/dbus-errors.h [new file with mode: 0644]
dbus/dbus-internals.c
dbus/dbus-internals.h
dbus/dbus-list.c
dbus/dbus-list.h
dbus/dbus-message-internal.h [new file with mode: 0644]
dbus/dbus-message.c
dbus/dbus-message.h
dbus/dbus-server-protected.h [new file with mode: 0644]
dbus/dbus-server-unix.c [new file with mode: 0644]
dbus/dbus-server-unix.h [new file with mode: 0644]
dbus/dbus-server.c [new file with mode: 0644]
dbus/dbus-server.h [new file with mode: 0644]
dbus/dbus-transport-protected.h [new file with mode: 0644]
dbus/dbus-transport-unix.c [new file with mode: 0644]
dbus/dbus-transport-unix.h [new file with mode: 0644]
dbus/dbus-transport.c [new file with mode: 0644]
dbus/dbus-transport.h [new file with mode: 0644]
dbus/dbus-types.h
dbus/dbus-watch.c [new file with mode: 0644]
dbus/dbus-watch.h [new file with mode: 0644]
dbus/dbus.h
doc/.cvsignore [new file with mode: 0644]
test/.cvsignore [new file with mode: 0644]
test/Makefile.am
test/echo-client.c [new file with mode: 0644]
test/echo-server.c [new file with mode: 0644]
test/watch.c [new file with mode: 0644]
test/watch.h [new file with mode: 0644]

diff --git a/.cvsignore b/.cvsignore
new file mode 100644 (file)
index 0000000..7487721
--- /dev/null
@@ -0,0 +1,16 @@
+config.log
+config.status
+config.sub
+configure
+*.pc
+libtool
+ltmain.sh
+stamp-h1
+Doxyfile
+Makefile
+Makefile.in
+aclocal.m4
+autom4te.cache
+config.guess
+config.h
+config.h.in
index d0a4b1d..e659c74 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,34 @@
+2002-11-24  Havoc Pennington  <hp@pobox.com>
+
+        * test/echo-client.c, test/echo-server.c: cheesy test 
+       clients.
+       
+       * configure.in (AC_CHECK_FUNCS): check for writev
+
+       * dbus/dbus-message.c (_dbus_message_get_network_data): new
+       function
+
+       * dbus/dbus-list.c (_dbus_list_foreach): new function
+
+       * dbus/dbus-internals.c (_dbus_verbose): new function
+
+       * dbus/dbus-server.c, dbus/dbus-server.h: public object
+       representing a server that listens for connections.
+
+       * dbus/.cvsignore: create
+
+       * dbus/dbus-errors.h, dbus/dbus-errors.c:
+       public API for reporting errors
+
+       * dbus/dbus-connection.h, dbus/dbus-connection.c:
+       public object representing a connection that 
+       sends/receives messages. (Same object used for 
+       both client and server.)
+
+       * dbus/dbus-transport.h, dbus/dbus-transport.c:
+       Basic abstraction for different kinds of stream
+       that we might read/write messages from.
+
 2002-11-23  Havoc Pennington  <hp@pobox.com>
 
        * dbus/dbus-internals.h (_DBUS_INT_MAX): add _DBUS_INT_MIN 
diff --git a/bus/.cvsignore b/bus/.cvsignore
new file mode 100644 (file)
index 0000000..8b96613
--- /dev/null
@@ -0,0 +1,7 @@
+.deps
+.libs
+Makefile
+Makefile.in
+*.lo
+*.la
+dbus-daemon-1
index d8ba3c6..4440040 100644 (file)
@@ -102,6 +102,10 @@ AC_C_BIGENDIAN
 
 AC_CHECK_FUNCS(vsnprintf vasprintf)
 
+dnl check for writev header and writev function so we're 
+dnl good to go if HAVE_WRITEV gets defined.
+AC_CHECK_HEADERS(sys/uio.h, [AC_CHECK_FUNCS(writev)])
+
 DBUS_CLIENT_CFLAGS=
 DBUS_CLIENT_LIBS=
 AC_SUBST(DBUS_CLIENT_CFLAGS)
@@ -112,6 +116,11 @@ DBUS_BUS_LIBS=
 AC_SUBST(DBUS_BUS_CFLAGS)
 AC_SUBST(DBUS_BUS_LIBS)
 
+DBUS_TEST_CFLAGS=
+DBUS_TEST_LIBS=
+AC_SUBST(DBUS_TEST_CFLAGS)
+AC_SUBST(DBUS_TEST_LIBS)
+
 AC_OUTPUT([
 Makefile
 Doxyfile
diff --git a/dbus/.cvsignore b/dbus/.cvsignore
new file mode 100644 (file)
index 0000000..4ebd42b
--- /dev/null
@@ -0,0 +1,7 @@
+.deps
+.libs
+Makefile
+Makefile.in
+*.lo
+*.la
+dbus-test
index 46d703f..f830bb7 100644 (file)
@@ -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 (file)
index 0000000..7260bde
--- /dev/null
@@ -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 (file)
index 0000000..f60e53a
--- /dev/null
@@ -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 (file)
index 0000000..ceb5728
--- /dev/null
@@ -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 (file)
index 0000000..1214c1f
--- /dev/null
@@ -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 (file)
index 0000000..d45e2f3
--- /dev/null
@@ -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 */
index 238baa8..e90703e 100644 (file)
 #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.)
  * @{
  *
  * @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
  *
  *
  * 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;
+}
+
 /** @} */
index 74bae96..5d00139 100644 (file)
@@ -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
 
 #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;
 
index 0b8e5a4..31e5ae3 100644 (file)
@@ -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
index d314ad5..8de4000 100644 (file)
@@ -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 (file)
index 0000000..5b1aea6
--- /dev/null
@@ -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 */
index a52d709..1e95bec 100644 (file)
  *
  */
 
+#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
 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);
 }
 
 /** @} */
index ce61838..69d74e9 100644 (file)
@@ -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 (file)
index 0000000..fb57c2e
--- /dev/null
@@ -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 (file)
index 0000000..277e00a
--- /dev/null
@@ -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 (file)
index 0000000..43614cc
--- /dev/null
@@ -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 (file)
index 0000000..c4b36bd
--- /dev/null
@@ -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 (file)
index 0000000..83f7610
--- /dev/null
@@ -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 (file)
index 0000000..e6c696f
--- /dev/null
@@ -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 (file)
index 0000000..869aa33
--- /dev/null
@@ -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 (file)
index 0000000..c483f05
--- /dev/null
@@ -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 (file)
index 0000000..8549887
--- /dev/null
@@ -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 (file)
index 0000000..84c1f8d
--- /dev/null
@@ -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 */
index d12b410..e55e005 100644 (file)
 #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 (file)
index 0000000..8f3c16b
--- /dev/null
@@ -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 (file)
index 0000000..869605a
--- /dev/null
@@ -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 */
index c03eb31..8bebaf5 100644 (file)
 
 #define DBUS_INSIDE_DBUS_H 1
 
+#include <dbus/dbus-connection.h>
+#include <dbus/dbus-errors.h>
 #include <dbus/dbus-macros.h>
 #include <dbus/dbus-message.h>
+#include <dbus/dbus-server.h>
 #include <dbus/dbus-types.h>
 
 #undef DBUS_INSIDE_DBUS_H
@@ -42,4 +45,4 @@
 /** @} */
 
 
-#endif /* DBUS_H
+#endif /* DBUS_H */
diff --git a/doc/.cvsignore b/doc/.cvsignore
new file mode 100644 (file)
index 0000000..444a9eb
--- /dev/null
@@ -0,0 +1,8 @@
+.deps
+.libs
+Makefile
+Makefile.in
+*.lo
+*.la
+*.o
+api
\ No newline at end of file
diff --git a/test/.cvsignore b/test/.cvsignore
new file mode 100644 (file)
index 0000000..53012e2
--- /dev/null
@@ -0,0 +1,9 @@
+.deps
+.libs
+Makefile
+Makefile.in
+*.lo
+*.la
+*.o
+echo-server
+echo-client
index 6e42b47..8f7d8e1 100644 (file)
@@ -1,4 +1,23 @@
 
 if DBUS_BUILD_TESTS
 
+INCLUDES=$(DBUS_TEST_CFLAGS)
+
+noinst_PROGRAMS= echo-client echo-server
+
+echo_client_SOURCES=                           \
+       echo-client.c                           \
+       watch.c                                 \
+       watch.h
+
+echo_server_SOURCES=                           \
+       echo-server.c                           \
+       watch.c                                 \
+       watch.h
+
+TEST_LIBS=$(DBUS_TEST_LIBS) $(top_builddir)/dbus/libdbus-convenience.la $(top_builddir)/dbus/libdbus-1.la
+
+echo_client_LDADD=$(TEST_LIBS)
+echo_server_LDADD=$(TEST_LIBS)
+
 endif
diff --git a/test/echo-client.c b/test/echo-client.c
new file mode 100644 (file)
index 0000000..5c6ee42
--- /dev/null
@@ -0,0 +1,41 @@
+#include <dbus/dbus.h>
+#include <stdio.h>
+#include "watch.h"
+
+int
+main (int    argc,
+      char **argv)
+{
+  DBusConnection *connection;
+  DBusResultCode result;
+  DBusMessage *message;
+  
+  if (argc < 2)
+    {
+      fprintf (stderr, "Give the server address as an argument\n");
+      return 1;
+    }
+  
+  connection = dbus_connection_open (argv[1], &result);
+  if (connection == NULL)
+    {
+      fprintf (stderr, "Failed to open connection to %s: %s\n",
+               argv[1], dbus_result_to_string (result));
+      return 1;
+    }
+
+  setup_connection (connection);
+
+  /* Send a message to get things going */
+  message = dbus_message_new ();
+  dbus_connection_send_message (connection,
+                                message,
+                                NULL);
+  dbus_message_unref (message);
+  
+  do_mainloop ();
+
+  dbus_connection_unref (connection);
+  
+  return 0;
+}
diff --git a/test/echo-server.c b/test/echo-server.c
new file mode 100644 (file)
index 0000000..99f97ff
--- /dev/null
@@ -0,0 +1,48 @@
+#include <dbus/dbus.h>
+#include <stdio.h>
+#include "watch.h"
+
+static void
+new_connection_callback (DBusServer     *server,
+                         DBusConnection *new_connection,
+                         void           *data)
+{
+  printf ("Got new connection\n");
+
+  setup_connection (new_connection);
+}
+
+int
+main (int    argc,
+      char **argv)
+{
+  DBusServer *server;
+  DBusResultCode result;
+
+  if (argc < 2)
+    {
+      fprintf (stderr, "Give the server address as an argument\n");
+      return 1;
+    }
+
+  server = dbus_server_listen (argv[1], &result);
+  if (server == NULL)
+    {
+      fprintf (stderr, "Failed to start server on %s: %s\n",
+               argv[1], dbus_result_to_string (result));
+      return 1;
+    }
+
+  setup_server (server);
+
+  dbus_server_set_new_connection_function (server,
+                                           new_connection_callback,
+                                           NULL, NULL);
+  
+  do_mainloop ();
+
+  dbus_server_disconnect (server);
+  dbus_server_unref (server);
+  
+  return 0;
+}
diff --git a/test/watch.c b/test/watch.c
new file mode 100644 (file)
index 0000000..df26855
--- /dev/null
@@ -0,0 +1,268 @@
+#include "watch.h"
+#include <stdio.h>
+
+#define DBUS_COMPILATION /* cheat and use DBusList */
+#include <dbus/dbus-list.h>
+#undef DBUS_COMPILATION
+
+/* Cheesy main loop used in test programs.  Any real app would use the
+ * GLib or Qt or other non-sucky main loops.
+ */ 
+
+#undef MAX
+#define MAX(a, b)  (((a) > (b)) ? (a) : (b))
+
+static DBusList *watches = NULL;
+static dbus_bool_t exited = FALSE;
+static DBusList *connections = NULL;
+
+typedef enum
+{
+  WATCH_CONNECTION,
+  WATCH_SERVER
+} WatchType;
+
+typedef struct
+{
+  WatchType type;
+  void *data;
+} WatchData;
+
+static void
+add_connection_watch (DBusWatch      *watch,
+                      DBusConnection *connection)
+{
+  WatchData *wd;
+
+  wd = dbus_new0 (WatchData, 1);
+  wd->type = WATCH_CONNECTION;
+  wd->data = connection;
+  
+  _dbus_list_append (&watches, watch);
+  dbus_watch_set_data (watch, wd, dbus_free);
+}
+
+static void
+remove_connection_watch (DBusWatch      *watch,
+                         DBusConnection *connection)
+{
+  _dbus_list_remove (&watches, watch);
+  dbus_watch_set_data (watch, NULL, NULL);
+}
+
+static void
+add_server_watch (DBusWatch      *watch,
+                  DBusServer     *server)
+{
+  WatchData *wd;
+  
+  wd = dbus_new0 (WatchData, 1);
+  wd->type = WATCH_SERVER;
+  wd->data = server;
+  
+  _dbus_list_append (&watches, watch);
+
+  dbus_watch_set_data (watch, wd, dbus_free);
+}
+
+static void
+remove_server_watch (DBusWatch      *watch,
+                     DBusServer     *server)
+{
+  _dbus_list_remove (&watches, watch);
+  dbus_watch_set_data (watch, NULL, NULL);
+}
+
+static int count = 0;
+
+static void
+check_messages (void)
+{
+  DBusList *link;
+  
+  link = _dbus_list_get_first_link (&connections);
+  while (link != NULL)
+    {
+      DBusList *next = _dbus_list_get_next_link (&connections, link);
+      DBusConnection *connection = link->data;
+      DBusMessage *message;
+      
+      while ((message = dbus_connection_pop_message (connection)))
+        {
+          DBusMessage *reply;
+
+          printf ("Received message %d, sending reply\n", count);
+          
+          reply = dbus_message_new ();
+          dbus_connection_send_message (connection,
+                                        reply,
+                                        NULL);
+          dbus_message_unref (reply);
+
+          dbus_message_unref (message);
+
+          count += 1;
+          if (count > 100)
+            {
+              printf ("Saw %d messages, exiting\n", count);
+              quit_mainloop ();
+            }
+        }
+      
+      link = next;
+    }
+}
+
+void
+do_mainloop (void)
+{
+  /* Of course with any real app you'd use GMainLoop or
+   * QSocketNotifier and not have to see all this crap.
+   */
+  
+  while (!exited && watches != NULL)
+    {
+      fd_set read_set;
+      fd_set write_set;
+      fd_set err_set;
+      int max_fd;
+      DBusList *link;
+
+      check_messages ();
+      
+      FD_ZERO (&read_set);
+      FD_ZERO (&write_set);
+      FD_ZERO (&err_set);
+
+      max_fd = -1;
+
+      link = _dbus_list_get_first_link (&watches);
+      while (link != NULL)
+        {
+          DBusList *next = _dbus_list_get_next_link (&watches, link);
+          int fd;
+          DBusWatch *watch;
+          unsigned int flags;
+          
+          watch = link->data;
+          
+          fd = dbus_watch_get_fd (watch);
+          flags = dbus_watch_get_flags (watch);
+          
+          max_fd = MAX (max_fd, fd);
+          
+          if (flags & DBUS_WATCH_READABLE)
+            FD_SET (fd, &read_set);
+
+          if (flags & DBUS_WATCH_WRITABLE)
+            FD_SET (fd, &write_set);
+
+          FD_SET (fd, &err_set);
+          
+          link = next;
+        }
+
+      select (max_fd + 1, &read_set, &write_set, &err_set, NULL);
+
+      link = _dbus_list_get_first_link (&watches);
+      while (link != NULL)
+        {
+          DBusList *next = _dbus_list_get_next_link (&watches, link);
+          int fd;
+          DBusWatch *watch;
+          unsigned int flags;
+          unsigned int condition;
+          
+          watch = link->data;
+          
+          fd = dbus_watch_get_fd (watch);
+          flags = dbus_watch_get_flags (watch);
+
+          condition = 0;
+          
+          if ((flags & DBUS_WATCH_READABLE) &&
+              FD_ISSET (fd, &read_set))
+            condition |= DBUS_WATCH_READABLE;
+
+          if ((flags & DBUS_WATCH_WRITABLE) &&
+              FD_ISSET (fd, &write_set))
+            condition |= DBUS_WATCH_WRITABLE;
+
+          if (FD_ISSET (fd, &err_set))
+            condition |= DBUS_WATCH_ERROR;
+
+          if (condition != 0)
+            {
+              WatchData *wd;
+
+              wd = dbus_watch_get_data (watch);
+
+              if (wd->type == WATCH_CONNECTION)
+                {
+                  DBusConnection *connection = wd->data;
+
+                  dbus_connection_handle_watch (connection,
+                                                watch,
+                                                condition);
+                }
+              else if (wd->type == WATCH_SERVER)
+                {
+                  DBusServer *server = wd->data;
+                  
+                  dbus_server_handle_watch (server,
+                                            watch,
+                                            condition);
+                }
+            }
+          
+          link = next;
+        }
+    }
+}
+
+void
+quit_mainloop (void)
+{
+  exited = TRUE;
+}
+
+static void
+error_handler (DBusConnection *connection,
+               DBusResultCode  error_code,
+               void           *data)
+{
+  fprintf (stderr,
+           "Error on connection: %s\n",
+           dbus_result_to_string (error_code));
+
+  _dbus_list_remove (&connections, connection);
+  dbus_connection_unref (connection);
+  quit_mainloop ();
+}
+
+void
+setup_connection (DBusConnection *connection)
+{
+  dbus_connection_set_watch_functions (connection,
+                                       (DBusAddWatchFunction) add_connection_watch,
+                                       (DBusRemoveWatchFunction) remove_connection_watch,
+                                       connection,
+                                       NULL);
+
+  dbus_connection_set_error_function (connection,
+                                      error_handler,
+                                      NULL, NULL);
+
+  dbus_connection_ref (connection);
+  _dbus_list_append (&connections, connection);
+}
+
+void
+setup_server (DBusServer *server)
+{
+  dbus_server_set_watch_functions (server,
+                                   (DBusAddWatchFunction) add_server_watch,
+                                   (DBusRemoveWatchFunction) remove_server_watch,
+                                   server,
+                                   NULL);
+}
diff --git a/test/watch.h b/test/watch.h
new file mode 100644 (file)
index 0000000..a9ad083
--- /dev/null
@@ -0,0 +1,15 @@
+/* Cheesy main loop thingy used by the test programs */
+
+#ifndef WATCH_H
+#define WATCH_H
+
+#include <dbus/dbus.h>
+
+void do_mainloop (void);
+
+void quit_mainloop (void);
+
+void setup_connection (DBusConnection *connection);
+void setup_server     (DBusServer *server);
+
+#endif