2003-01-18 Havoc Pennington <hp@pobox.com>
authorHavoc Pennington <hp@redhat.com>
Sun, 19 Jan 2003 03:33:35 +0000 (03:33 +0000)
committerHavoc Pennington <hp@redhat.com>
Sun, 19 Jan 2003 03:33:35 +0000 (03:33 +0000)
* dbus/dbus-transport-unix.c (unix_do_iteration): only do the
reading/writing if read_watch != NULL or write_watch != NULL.

* dbus/dbus-message.c (_dbus_message_loader_return_buffer): fix
the message loader code to actually load message->header and
message->body into the newly-created message.

* dbus/dbus-transport-unix.c (check_write_watch): fix a mem leak
in OOM case

* dbus/dbus-connection.c (dbus_connection_set_max_message_size)
(dbus_connection_get_max_message_size)
(dbus_connection_set_max_live_messages_size)
(dbus_connection_get_max_live_messages_size): implement some
resource limitation functions

* dbus/dbus-resources.c: new file implementing some of the
resource limits stuff

* dbus/dbus-message.c (dbus_message_iter_get_byte_array): add
missing docs, add @todo to handle OOM etc.

* dbus/dbus-marshal.c (_dbus_demarshal_byte_array): add missing
docs

15 files changed:
ChangeLog
dbus/Makefile.am
dbus/dbus-connection.c
dbus/dbus-connection.h
dbus/dbus-marshal.c
dbus/dbus-message-internal.h
dbus/dbus-message.c
dbus/dbus-resources.c [new file with mode: 0644]
dbus/dbus-resources.h [new file with mode: 0644]
dbus/dbus-transport-protected.h
dbus/dbus-transport-unix.c
dbus/dbus-transport.c
dbus/dbus-transport.h
test/echo-server.c
test/watch.c

index e80783f..3cccd3a 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,32 @@
 2003-01-18  Havoc Pennington  <hp@pobox.com>
 
+       * dbus/dbus-transport-unix.c (unix_do_iteration): only do the
+       reading/writing if read_watch != NULL or write_watch != NULL.
+
+       * dbus/dbus-message.c (_dbus_message_loader_return_buffer): fix
+       the message loader code to actually load message->header and
+       message->body into the newly-created message.
+
+       * dbus/dbus-transport-unix.c (check_write_watch): fix a mem leak
+       in OOM case
+
+       * dbus/dbus-connection.c (dbus_connection_set_max_message_size) 
+       (dbus_connection_get_max_message_size) 
+       (dbus_connection_set_max_live_messages_size) 
+       (dbus_connection_get_max_live_messages_size): implement some
+       resource limitation functions
+
+       * dbus/dbus-resources.c: new file implementing some of the
+       resource limits stuff
+
+       * dbus/dbus-message.c (dbus_message_iter_get_byte_array): add
+       missing docs, add @todo to handle OOM etc.
+
+       * dbus/dbus-marshal.c (_dbus_demarshal_byte_array): add missing
+       docs
+       
+2003-01-18  Havoc Pennington  <hp@pobox.com>
+
        * dbus/dbus-connection.c (dbus_connection_unref): disconnect the 
        connection if it hasn't been already.
 
index 955b793..b9d97da 100644 (file)
@@ -28,6 +28,8 @@ libdbus_1_la_SOURCES=                         \
        dbus-message.c                          \
        dbus-message-handler.c                  \
        dbus-message-internal.h                 \
+       dbus-resources.c                        \
+       dbus-resources.h                        \
        dbus-server.c                           \
        dbus-server-protected.h                 \
        dbus-server-unix.c                      \
index 073da8f..5ffbc57 100644 (file)
@@ -1,7 +1,7 @@
 /* -*- mode: C; c-file-style: "gnu" -*- */
 /* dbus-connection.c DBusConnection object
  *
- * Copyright (C) 2002  Red Hat Inc.
+ * Copyright (C) 2002, 2003  Red Hat Inc.
  *
  * Licensed under the Academic Free License version 1.2
  * 
@@ -1274,4 +1274,74 @@ _dbus_connection_free_data_slots (DBusConnection *connection)
   connection->n_slots = 0;
 }
 
+/**
+ * Specifies the maximum size message this connection is allowed to
+ * receive. Larger messages will result in disconnecting the
+ * connection.
+ * 
+ * @param connection a #DBusConnection
+ * @param size maximum message size the connection can receive, in bytes
+ */
+void
+dbus_connection_set_max_message_size (DBusConnection *connection,
+                                      long            size)
+{
+  _dbus_transport_set_max_message_size (connection->transport,
+                                        size);
+}
+
+/**
+ * Gets the value set by dbus_connection_set_max_message_size().
+ *
+ * @param connection the connection
+ * @returns the max size of a single message
+ */
+long
+dbus_connection_get_max_message_size (DBusConnection *connection)
+{
+  return _dbus_transport_get_max_message_size (connection->transport);
+}
+
+/**
+ * Sets the maximum total number of bytes that can be used for all messages
+ * received on this connection. Messages count toward the maximum until
+ * they are finalized. When the maximum is reached, the connection will
+ * not read more data until some messages are finalized.
+ *
+ * The semantics of the maximum are: if outstanding messages are
+ * already above the maximum, additional messages will not be read.
+ * The semantics are not: if the next message would cause us to exceed
+ * the maximum, we don't read it. The reason is that we don't know the
+ * size of a message until after we read it.
+ *
+ * Thus, the max live messages size can actually be exceeded
+ * by up to the maximum size of a single message.
+ * 
+ * Also, if we read say 1024 bytes off the wire in a single read(),
+ * and that contains a half-dozen small messages, we may exceed the
+ * size max by that amount. But this should be inconsequential.
+ *
+ * @param connection the connection
+ * @param size the maximum size in bytes of all outstanding messages
+ */
+void
+dbus_connection_set_max_live_messages_size (DBusConnection *connection,
+                                            long            size)
+{
+  _dbus_transport_set_max_live_messages_size (connection->transport,
+                                              size);
+}
+
+/**
+ * Gets the value set by dbus_connection_set_max_live_messages_size().
+ *
+ * @param connection the connection
+ * @returns the max size of all live messages
+ */
+long
+dbus_connection_get_max_live_messages_size (DBusConnection *connection)
+{
+  return _dbus_transport_get_max_live_messages_size (connection->transport);
+}
+
 /** @} */
index d0211b4..31653a5 100644 (file)
@@ -132,6 +132,14 @@ dbus_bool_t dbus_connection_set_data           (DBusConnection   *connection,
 void*       dbus_connection_get_data           (DBusConnection   *connection,
                                                 int               slot);
 
+void dbus_connection_set_max_message_size       (DBusConnection *connection,
+                                                 long            size);
+long dbus_connection_get_max_message_size       (DBusConnection *connection);
+void dbus_connection_set_max_live_messages_size (DBusConnection *connection,
+                                                 long            size);
+long dbus_connection_get_max_live_messages_size (DBusConnection *connection);
+
+
 DBUS_END_DECLS;
 
 #endif /* DBUS_CONNECTION_H */
index a605580..395c2cc 100644 (file)
@@ -401,6 +401,19 @@ _dbus_demarshal_string (DBusString *str,
   return retval;
 }
 
+/**
+ * Demarshals a byte array.
+ *
+ * @todo Should probably demarshal to a DBusString,
+ * having memcpy() in here is Evil(tm).
+ *
+ * @param str the string containing the data
+ * @param byte_order the byte order
+ * @param pos the position in the string
+ * @param new_pos the new position of the string
+ * @param array_len length of the demarshaled data
+ * @returns the demarshaled data.
+ */
 unsigned char *
 _dbus_demarshal_byte_array (DBusString *str,
                            int         byte_order,
index c57af04..5ec7ad9 100644 (file)
@@ -24,6 +24,7 @@
 #define DBUS_MESSAGE_INTERNAL_H
 
 #include <dbus/dbus-message.h>
+#include <dbus/dbus-resources.h>
 
 DBUS_BEGIN_DECLS;
 
@@ -38,6 +39,9 @@ void _dbus_message_lock              (DBusMessage       *message);
 void _dbus_message_set_client_serial (DBusMessage       *message,
                                      dbus_int32_t       client_serial);
 
+void _dbus_message_add_size_counter  (DBusMessage       *message,
+                                      DBusCounter       *counter);
+
 DBusMessageLoader* _dbus_message_loader_new                   (void);
 void               _dbus_message_loader_ref                   (DBusMessageLoader  *loader);
 void               _dbus_message_loader_unref                 (DBusMessageLoader  *loader);
@@ -52,6 +56,10 @@ DBusMessage*       _dbus_message_loader_pop_message           (DBusMessageLoader
 
 dbus_bool_t        _dbus_message_loader_get_is_corrupted      (DBusMessageLoader  *loader);
 
+void               _dbus_message_loader_set_max_message_size  (DBusMessageLoader  *loader,
+                                                               long                size);
+long               _dbus_message_loader_get_max_message_size  (DBusMessageLoader  *loader);
+
 DBUS_END_DECLS;
 
 #endif /* DBUS_MESSAGE_H */
index 3bd9280..6206815 100644 (file)
@@ -1,7 +1,7 @@
 /* -*- mode: C; c-file-style: "gnu" -*- */
 /* dbus-message.c  DBusMessage object
  *
- * Copyright (C) 2002  Red Hat Inc.
+ * Copyright (C) 2002, 2003  Red Hat Inc.
  * Copyright (C) 2002, 2003  CodeFactory AB
  *
  * Licensed under the Academic Free License version 1.2
  */
 
 /**
- * The largest-length message we allow
- *
- * @todo match this up with whatever the protocol spec says.
- */
-#define _DBUS_MAX_MESSAGE_LENGTH (_DBUS_INT_MAX/16)
-
-/**
  * @brief Internals of DBusMessage
  * 
  * Object representing a message received from or to be sent to
@@ -72,6 +65,9 @@ struct DBusMessage
   
   dbus_int32_t client_serial; /**< Client serial or -1 if not set */
   dbus_int32_t reply_serial; /**< Reply serial or -1 if not set */
+
+  DBusCounter *size_counter; /**< Counter for the size of the message, or #NULL */
+  long size_counter_delta;   /**< Size we incremented the size counter by. */
   
   unsigned int locked : 1; /**< Message being sent, no modifications allowed. */
 };
@@ -127,6 +123,39 @@ _dbus_message_set_client_serial (DBusMessage  *message,
   message->client_serial = client_serial;
 }
 
+/**
+ * Adds a counter to be incremented immediately with the
+ * size of this message, and decremented by the size
+ * of this message when this message if finalized.
+ *
+ * @param message the message
+ * @param counter the counter
+ */
+void
+_dbus_message_add_size_counter (DBusMessage *message,
+                                DBusCounter *counter)
+{
+  _dbus_assert (message->size_counter == NULL); /* If this fails we may need to keep a list of
+                                                 * counters instead of just one
+                                                 */
+
+  message->size_counter = counter;
+  _dbus_counter_ref (message->size_counter);
+
+  /* When we can change message size, we may want to
+   * update this each time we do so, or we may want to
+   * just KISS like this.
+   */
+  message->size_counter_delta =
+    _dbus_string_get_length (&message->header) +
+    _dbus_string_get_length (&message->body);
+
+  _dbus_verbose ("message has size %ld\n",
+                 message->size_counter_delta);
+  
+  _dbus_counter_adjust (message->size_counter, message->size_counter_delta);
+}
+
 static void
 dbus_message_write_header (DBusMessage *message)
 {
@@ -253,7 +282,7 @@ dbus_message_new (const char *service,
   message->client_serial = -1;
   message->reply_serial = -1;
   
-  if (!_dbus_string_init (&message->header, _DBUS_MAX_MESSAGE_LENGTH))
+  if (!_dbus_string_init (&message->header, _DBUS_INT_MAX))
     {
       dbus_free (message->service);
       dbus_free (message->name);
@@ -261,7 +290,7 @@ dbus_message_new (const char *service,
       return NULL;
     }
 
-  if (!_dbus_string_init (&message->body, _DBUS_MAX_MESSAGE_LENGTH))
+  if (!_dbus_string_init (&message->body, _DBUS_INT_MAX))
     {
       dbus_free (message->service);
       dbus_free (message->name);
@@ -302,6 +331,13 @@ dbus_message_unref (DBusMessage *message)
   message->refcount -= 1;
   if (message->refcount == 0)
     {
+      if (message->size_counter != NULL)
+        {
+          _dbus_counter_adjust (message->size_counter,
+                                - message->size_counter_delta);
+          _dbus_counter_unref (message->size_counter);
+        }
+      
       _dbus_string_free (&message->header);
       _dbus_string_free (&message->body);
 
@@ -330,7 +366,7 @@ dbus_message_get_name (DBusMessage *message)
  * The list is terminated with 0.
  *
  * @param message the message
- * @param type of the first field
+ * @param first_field_type type of the first field
  * @param ... value of first field, list of additional type-value pairs
  * @returns #TRUE on success
  */
@@ -886,8 +922,21 @@ dbus_message_iter_get_double (DBusMessageIter *iter)
                                 iter->pos + 1, NULL);
 }
 
+/**
+ * Returns the byte array that the iterator may point to.
+ * Note that you need to check that the iterator points
+ * to a byte array prior to using this function.
+ *
+ * @todo this function should probably take "unsigned char **" as
+ * an out param argument, and return boolean or result code.
+ *
+ * @param iter the iterator
+ * @param len return location for length of byte array
+ * @returns the byte array
+ */
 unsigned char *
-dbus_message_iter_get_byte_array (DBusMessageIter *iter, int *len)
+dbus_message_iter_get_byte_array (DBusMessageIter *iter,
+                                  int             *len)
 {
   _dbus_assert (dbus_message_iter_get_field_type (iter) == DBUS_TYPE_BYTE_ARRAY);
 
@@ -926,6 +975,8 @@ struct DBusMessageLoader
   DBusString data;     /**< Buffered data */
   
   DBusList *messages;  /**< Complete messages. */
+
+  long max_message_size; /**< Maximum size of a message */
   
   unsigned int buffer_outstanding : 1; /**< Someone is using the buffer to read */
 
@@ -959,8 +1010,13 @@ _dbus_message_loader_new (void)
   if (loader == NULL)
     return NULL;
   
-  loader->refcount = 1;  
+  loader->refcount = 1;
 
+  /* Try to cap message size at something that won't *totally* hose
+   * the system if we have a couple of them.
+   */
+  loader->max_message_size = _DBUS_ONE_MEGABYTE * 32;
+  
   if (!_dbus_string_init (&loader->data, _DBUS_INT_MAX))
     {
       dbus_free (loader);
@@ -1190,10 +1246,10 @@ _dbus_message_loader_return_buffer (DBusMessageLoader  *loader,
       header_len = _dbus_unpack_int32 (byte_order, header_data + 4);
       body_len = _dbus_unpack_int32 (byte_order, header_data + 8);
 
-      if (header_len + body_len > _DBUS_MAX_MESSAGE_LENGTH)
+      if (header_len + body_len > loader->max_message_size)
        {
           _dbus_verbose ("Message claimed length header = %d body = %d exceeds max message length %d\n",
-                         header_len, body_len, _DBUS_MAX_MESSAGE_LENGTH);
+                         header_len, body_len, loader->max_message_size);
          loader->corrupted = TRUE;
          return;
        }
@@ -1220,13 +1276,40 @@ _dbus_message_loader_return_buffer (DBusMessageLoader  *loader,
          
          if (message == NULL)
             break; /* ugh, postpone this I guess. */
+                 
+         if (!_dbus_list_append (&loader->messages, message))
+            {
+              dbus_message_unref (message);
+              break;
+            }
 
-         _dbus_string_copy (&loader->data, header_len, &message->body, 0);
-         _dbus_message_set_client_serial (message, client_serial);
-         
-         _dbus_list_append (&loader->messages, message);
-         _dbus_string_delete (&loader->data, 0, header_len + body_len);
+          _dbus_assert (_dbus_string_get_length (&message->header) == 0);
+          _dbus_assert (_dbus_string_get_length (&message->body) == 0);
+          
+         if (!_dbus_string_move_len (&loader->data, 0, header_len, &message->header, 0))
+            {
+              _dbus_list_remove_last (&loader->messages, message);
+              dbus_message_unref (message);
+              break;
+            }
+
+         if (!_dbus_string_move_len (&loader->data, 0, body_len, &message->body, 0))
+            {
+              dbus_bool_t result;
+
+              /* put the header back, we'll try again later */
+              result = _dbus_string_copy_len (&message->header, 0, header_len,
+                                              &loader->data, 0);
+              _dbus_assert (result); /* because DBusString never reallocs smaller */
 
+              _dbus_list_remove_last (&loader->messages, message);
+              dbus_message_unref (message);
+              break;
+            }
+
+          _dbus_assert (_dbus_string_get_length (&message->header) == header_len);
+          _dbus_assert (_dbus_string_get_length (&message->body) == body_len);
+          
          _dbus_verbose ("Loaded message %p\n", message);         
        }
       else
@@ -1264,6 +1347,31 @@ _dbus_message_loader_get_is_corrupted (DBusMessageLoader *loader)
   return loader->corrupted;
 }
 
+/**
+ * Sets the maximum size message we allow.
+ *
+ * @param loader the loader
+ * @param size the max message size in bytes
+ */
+void
+_dbus_message_loader_set_max_message_size (DBusMessageLoader  *loader,
+                                           long                size)
+{
+  loader->max_message_size = size;
+}
+
+/**
+ * Gets the maximum allowed message size in bytes.
+ *
+ * @param loader the loader
+ * @returns max size in bytes
+ */
+long
+_dbus_message_loader_get_max_message_size (DBusMessageLoader  *loader)
+{
+  return loader->max_message_size;
+}
+
 /** @} */
 #ifdef DBUS_BUILD_TESTS
 #include "dbus-test.h"
diff --git a/dbus/dbus-resources.c b/dbus/dbus-resources.c
new file mode 100644 (file)
index 0000000..edc2e78
--- /dev/null
@@ -0,0 +1,191 @@
+/* -*- mode: C; c-file-style: "gnu" -*- */
+/* dbus-resources.c Resource tracking/limits
+ *
+ * Copyright (C) 2003  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/dbus-resources.h>
+#include <dbus/dbus-internals.h>
+
+/**
+ * @defgroup DBusResources Resource limits related code
+ * @ingroup  DBusInternals
+ * @brief DBusCounter and other stuff related to resource limits
+ *
+ * Types and functions related to tracking resource limits,
+ * such as the maximum amount of memory a connection can use
+ * for messages, etc.
+ */
+
+/**
+ * @defgroup DBusResourcesInternals Resource limits implementation details
+ * @ingroup  DBusInternals
+ * @brief Resource limits implementation details
+ *
+ * Implementation details of resource limits code.
+ *
+ * @{
+ */
+
+/**
+ * @brief Internals of DBusCounter.
+ * 
+ * DBusCounter internals. DBusCounter is an opaque object, it must be
+ * used via accessor functions.
+ */
+struct DBusCounter
+{
+  int refcount;  /**< reference count */
+
+  long value;    /**< current counter value */
+
+  long notify_guard_value; /**< call notify function when crossing this value */
+  DBusCounterNotifyFunction notify_function; /**< notify function */
+  void *notify_data; /**< data for notify function */
+};
+
+/** @} */  /* end of resource limits internals docs */
+
+/**
+ * @addtogroup DBusResources
+ * @{
+ */
+
+/**
+ * Creates a new DBusCounter. DBusCounter is used
+ * to count usage of some resource such as memory.
+ *
+ * @returns new counter or #NULL on failure
+ */
+DBusCounter*
+_dbus_counter_new (void)
+{
+  DBusCounter *counter;
+
+  counter = dbus_new (DBusCounter, 1);
+  if (counter == NULL)
+    return NULL;
+  
+  counter->refcount = 1;
+  counter->value = 0;
+
+  counter->notify_guard_value = 0;
+  counter->notify_function = NULL;
+  counter->notify_data = NULL;
+  
+  return counter;
+}
+
+/**
+ * Increments refcount of the counter
+ *
+ * @param counter the counter
+ */
+void
+_dbus_counter_ref (DBusCounter *counter)
+{
+  _dbus_assert (counter->refcount > 0);
+  
+  counter->refcount += 1;
+}
+
+/**
+ * Decrements refcount of the counter and possibly
+ * finalizes the counter.
+ *
+ * @param counter the counter
+ */
+void
+_dbus_counter_unref (DBusCounter *counter)
+{
+  _dbus_assert (counter->refcount > 0);
+
+  counter->refcount -= 1;
+
+  if (counter->refcount == 0)
+    {
+      
+      dbus_free (counter);
+    }
+}
+
+/**
+ * Adjusts the value of the counter by the given
+ * delta which may be positive or negative.
+ * Calls the notify function from _dbus_counter_set_notify()
+ * if that function has been specified.
+ *
+ * @param counter the counter
+ * @param delta value to add to the counter's current value
+ */
+void
+_dbus_counter_adjust (DBusCounter *counter,
+                      long         delta)
+{
+  long old = counter->value;
+  
+  counter->value += delta;
+
+#if 0
+  _dbus_verbose ("Adjusting counter %ld by %ld = %ld\n",
+                 old, delta, counter->value);
+#endif
+  
+  if (counter->notify_function != NULL &&
+      ((old < counter->notify_guard_value &&
+        counter->value >= counter->notify_guard_value) ||
+       (old >= counter->notify_guard_value &&
+        counter->value < counter->notify_guard_value)))
+    (* counter->notify_function) (counter, counter->notify_data);
+}
+
+/**
+ * Gets the current value of the counter.
+ *
+ * @param counter the counter
+ * @returns its current value
+ */
+long
+_dbus_counter_get_value (DBusCounter *counter)
+{
+  return counter->value;
+}
+
+/**
+ * Sets the notify function for this counter; the notify function is
+ * called whenever the counter's value crosses the guard value in
+ * either direction (moving up, or moving down).
+ *
+ * @param counter the counter
+ * @param guard_value the value we're notified if the counter crosses
+ * @param function function to call in order to notify
+ * @param user_data data to pass to the function
+ */
+void
+_dbus_counter_set_notify (DBusCounter               *counter,
+                          long                       guard_value,
+                          DBusCounterNotifyFunction  function,
+                          void                      *user_data)
+{
+  counter->notify_guard_value = guard_value;
+  counter->notify_function = function;
+  counter->notify_data = user_data;
+}
+
+/** @} */  /* end of resource limits exported API */
diff --git a/dbus/dbus-resources.h b/dbus/dbus-resources.h
new file mode 100644 (file)
index 0000000..26a5b98
--- /dev/null
@@ -0,0 +1,52 @@
+/* -*- mode: C; c-file-style: "gnu" -*- */
+/* dbus-resources.h Resource tracking/limits
+ *
+ * Copyright (C) 2003  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_RESOURCES_H
+#define DBUS_RESOURCES_H
+
+#include <dbus/dbus-macros.h>
+#include <dbus/dbus-errors.h>
+#include <dbus/dbus-connection.h>
+
+DBUS_BEGIN_DECLS;
+
+typedef struct DBusCounter DBusCounter;
+
+typedef void (* DBusCounterNotifyFunction) (DBusCounter *counter,
+                                            void        *user_data);
+
+DBusCounter* _dbus_counter_new       (void);
+void         _dbus_counter_ref       (DBusCounter *counter);
+void         _dbus_counter_unref     (DBusCounter *counter);
+void         _dbus_counter_adjust    (DBusCounter *counter,
+                                      long         delta);
+long         _dbus_counter_get_value (DBusCounter *counter);
+
+void _dbus_counter_set_notify (DBusCounter               *counter,
+                               long                       guard_value,
+                               DBusCounterNotifyFunction  function,
+                               void                      *user_data);
+
+
+DBUS_END_DECLS;
+
+#endif /* DBUS_RESOURCES_H */
index b9bbb40..46dfe14 100644 (file)
@@ -28,6 +28,7 @@
 #include <dbus/dbus-transport.h>
 #include <dbus/dbus-message-internal.h>
 #include <dbus/dbus-auth.h>
+#include <dbus/dbus-resources.h>
 
 DBUS_BEGIN_DECLS;
 
@@ -63,6 +64,9 @@ struct DBusTransportVTable
   /**< Called to do a single "iteration" (block on select/poll
    * followed by reading or writing data).
    */
+
+  void (* live_messages_changed) (DBusTransport *transport);
+  /**< Outstanding messages counter changed */
 };
 
 struct DBusTransport
@@ -77,7 +81,11 @@ struct DBusTransport
 
   DBusAuth *auth;                             /**< Authentication conversation */
 
-  DBusCredentials credentials;                /**< Credentials of other end */
+  DBusCredentials credentials;                /**< Credentials of other end */  
+
+  long max_live_messages_size;                /**< Max total size of received messages. */
+
+  DBusCounter *live_messages_size;            /**< Counter for size of all live messages. */
   
   unsigned int disconnected : 1;              /**< #TRUE if we are disconnected. */
   unsigned int authenticated : 1;             /**< Cache of auth state; use _dbus_transport_get_is_authenticated() to query value */
index be1ab44..ef50863 100644 (file)
@@ -1,7 +1,7 @@
 /* -*- mode: C; c-file-style: "gnu" -*- */
 /* dbus-transport-unix.c UNIX socket subclasses of DBusTransport
  *
- * Copyright (C) 2002  Red Hat Inc.
+ * Copyright (C) 2002, 2003  Red Hat Inc.
  *
  * Licensed under the Academic Free License version 1.2
  * 
@@ -51,7 +51,7 @@ struct DBusTransportUnix
 {
   DBusTransport base;                   /**< Parent instance */
   int fd;                               /**< File descriptor. */
-  DBusWatch *watch;                     /**< Watch for readability. */
+  DBusWatch *read_watch;                /**< Watch for readability. */
   DBusWatch *write_watch;               /**< Watch for writability. */
 
   int max_bytes_read_per_iteration;     /**< To avoid blocking too long. */
@@ -71,14 +71,14 @@ free_watches (DBusTransport *transport)
 {
   DBusTransportUnix *unix_transport = (DBusTransportUnix*) transport;
   
-  if (unix_transport->watch)
+  if (unix_transport->read_watch)
     {
       if (transport->connection)
         _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;
+                                       unix_transport->read_watch);
+      _dbus_watch_invalidate (unix_transport->read_watch);
+      _dbus_watch_unref (unix_transport->read_watch);
+      unix_transport->read_watch = NULL;
     }
 
   if (unix_transport->write_watch)
@@ -103,7 +103,7 @@ unix_finalize (DBusTransport *transport)
   
   _dbus_transport_finalize_base (transport);
 
-  _dbus_assert (unix_transport->watch == NULL);
+  _dbus_assert (unix_transport->read_watch == NULL);
   _dbus_assert (unix_transport->write_watch == NULL);
   
   dbus_free (transport);
@@ -138,7 +138,7 @@ check_write_watch (DBusTransport *transport)
 
       /* we can maybe add it some other time, just silently bomb */
       if (unix_transport->write_watch == NULL)
-        return;
+        goto out;
 
       if (!_dbus_connection_add_watch (transport->connection,
                                        unix_transport->write_watch))
@@ -158,6 +158,63 @@ check_write_watch (DBusTransport *transport)
       unix_transport->write_watch = NULL;
     }
 
+ out:
+  _dbus_transport_unref (transport);
+}
+
+static void
+check_read_watch (DBusTransport *transport)
+{
+  DBusTransportUnix *unix_transport = (DBusTransportUnix*) transport;
+  dbus_bool_t need_read_watch;
+
+  if (transport->connection == NULL)
+    return;
+  
+  _dbus_transport_ref (transport);
+
+  need_read_watch =
+    _dbus_counter_get_value (transport->live_messages_size) < transport->max_live_messages_size;
+
+  if (transport->disconnected)
+    need_read_watch = FALSE;
+  
+  if (need_read_watch &&
+      unix_transport->read_watch == NULL)
+    {
+      _dbus_verbose ("Adding read watch to unix fd %d\n",
+                     unix_transport->fd);
+      
+      unix_transport->read_watch =
+        _dbus_watch_new (unix_transport->fd,
+                         DBUS_WATCH_READABLE);
+
+      /* we can maybe add it some other time, just silently bomb */
+      if (unix_transport->read_watch == NULL)
+        goto out;
+
+      if (!_dbus_connection_add_watch (transport->connection,
+                                       unix_transport->read_watch))
+        {
+          _dbus_watch_invalidate (unix_transport->read_watch);
+          _dbus_watch_unref (unix_transport->read_watch);
+          unix_transport->read_watch = NULL;
+        }
+    }
+  else if (!need_read_watch &&
+           unix_transport->read_watch != NULL)
+    {
+      _dbus_verbose ("Removing read watch from unix fd %d\n",
+                     unix_transport->fd);
+      
+      _dbus_connection_remove_watch (transport->connection,
+                                     unix_transport->read_watch);
+      _dbus_watch_invalidate (unix_transport->read_watch);
+      _dbus_watch_unref (unix_transport->read_watch);
+      unix_transport->read_watch = NULL;
+    }
+
+ out:
   _dbus_transport_unref (transport);
 }
 
@@ -178,7 +235,8 @@ queue_messages (DBusTransport *transport)
   while ((message = _dbus_message_loader_pop_message (transport->loader)))
     {
       _dbus_verbose ("queueing received message %p\n", message);
-      
+
+      _dbus_message_add_size_counter (message, transport->live_messages_size);
       _dbus_connection_queue_received_message (transport->connection,
                                                message);
       dbus_message_unref (message);
@@ -189,6 +247,9 @@ queue_messages (DBusTransport *transport)
       _dbus_verbose ("Corrupted message stream, disconnecting\n");
       do_io_error (transport);
     }
+
+  /* check read watch in case we've now exceeded max outstanding messages */
+  check_read_watch (transport);
 }
 
 /* return value is whether we successfully read any new data. */
@@ -526,6 +587,12 @@ do_writing (DBusTransport *transport)
                          total, unix_transport->max_bytes_written_per_iteration);
           goto out;
         }
+
+      if (unix_transport->write_watch == NULL)
+        {
+          _dbus_verbose ("write watch removed, not writing more stuff\n");
+          goto out;
+        }
       
       message = _dbus_connection_get_message_to_send (transport->connection);
       _dbus_assert (message != NULL);
@@ -650,6 +717,11 @@ do_reading (DBusTransport *transport)
   total = 0;
 
  again:
+
+  /* See if we've exceeded max messages and need to disable reading */
+  check_read_watch (transport);
+  if (unix_transport->read_watch == NULL)
+    return;
   
   if (total > unix_transport->max_bytes_read_per_iteration)
     {
@@ -761,7 +833,7 @@ unix_handle_watch (DBusTransport *transport,
 {
   DBusTransportUnix *unix_transport = (DBusTransportUnix*) transport;
 
-  _dbus_assert (watch == unix_transport->watch ||
+  _dbus_assert (watch == unix_transport->read_watch ||
                 watch == unix_transport->write_watch);
   
   if (flags & (DBUS_WATCH_HANGUP | DBUS_WATCH_ERROR))
@@ -770,7 +842,7 @@ unix_handle_watch (DBusTransport *transport,
       return;
     }
   
-  if (watch == unix_transport->watch &&
+  if (watch == unix_transport->read_watch &&
       (flags & DBUS_WATCH_READABLE))
     {
       _dbus_verbose ("handling read watch\n");
@@ -800,30 +872,7 @@ unix_disconnect (DBusTransport *transport)
 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);
-      _dbus_watch_unref (watch);
-      return;
-    }
-
-  unix_transport->watch = watch;
-
+  check_read_watch (transport);
   check_write_watch (transport);
 }
 
@@ -844,6 +893,11 @@ unix_do_iteration (DBusTransport *transport,
   fd_set read_set;
   fd_set write_set;
   dbus_bool_t do_select;
+
+  _dbus_verbose (" iteration flags = %s%s timeout = %d\n",
+                 flags & DBUS_ITERATION_DO_READING ? "read" : "",
+                 flags & DBUS_ITERATION_DO_WRITING ? "write" : "",
+                 timeout_milliseconds);
   
   /* "again" has to be up here because on EINTR the fd sets become
    * undefined
@@ -856,6 +910,9 @@ unix_do_iteration (DBusTransport *transport,
    * read/write messages, but regardless of those we may need to block
    * for reading/writing to do auth.  But if we do reading for auth,
    * we don't want to read any messages yet if not given DO_READING.
+   *
+   * Also, if read_watch == NULL or write_watch == NULL, we don't
+   * want to read/write so don't.
    */
 
   FD_ZERO (&read_set);
@@ -863,14 +920,15 @@ unix_do_iteration (DBusTransport *transport,
   
   if (_dbus_transport_get_is_authenticated (transport))
     {
-      if (flags & DBUS_ITERATION_DO_READING)
+      if (unix_transport->read_watch &&
+          (flags & DBUS_ITERATION_DO_READING))
         {
           FD_SET (unix_transport->fd, &read_set);
           do_select = TRUE;
         }
       
-      
-      if (flags & DBUS_ITERATION_DO_WRITING)
+      if (unix_transport->write_watch &&
+          (flags & DBUS_ITERATION_DO_WRITING))
         {
           FD_SET (unix_transport->fd, &write_set);
           do_select = TRUE;
@@ -957,13 +1015,21 @@ unix_do_iteration (DBusTransport *transport,
     }
 }
 
+static void
+unix_live_messages_changed (DBusTransport *transport)
+{
+  /* See if we should look for incoming messages again */
+  check_read_watch (transport);
+}
+
 static DBusTransportVTable unix_vtable = {
   unix_finalize,
   unix_handle_watch,
   unix_disconnect,
   unix_connection_set,
   unix_messages_pending,
-  unix_do_iteration
+  unix_do_iteration,
+  unix_live_messages_changed
 };
 
 /**
@@ -1009,6 +1075,7 @@ _dbus_transport_new_for_fd (int         fd,
   unix_transport->max_bytes_read_per_iteration = 2048;
   unix_transport->max_bytes_written_per_iteration = 2048;
 
+  check_read_watch ((DBusTransport*) unix_transport);
   check_write_watch ((DBusTransport*) unix_transport);
   
   return (DBusTransport*) unix_transport;
index 28326ba..9b367dc 100644 (file)
@@ -1,7 +1,7 @@
 /* -*- mode: C; c-file-style: "gnu" -*- */
 /* dbus-transport.c DBusTransport object (internal to D-BUS implementation)
  *
- * Copyright (C) 2002  Red Hat Inc.
+ * Copyright (C) 2002, 2003  Red Hat Inc.
  *
  * Licensed under the Academic Free License version 1.2
  * 
 #define DBUS_TRANSPORT_RELEASE_REF(t) \
   if ((t)->connection) dbus_connection_unref ((t)->connection); _dbus_transport_unref (t)
 
+static void
+live_messages_size_notify (DBusCounter *counter,
+                           void        *user_data)
+{
+  DBusTransport *transport = user_data;
+
+  DBUS_TRANSPORT_HOLD_REF (transport);
+
+#if 0
+  _dbus_verbose ("Counter value is now %d\n",
+                 (int) _dbus_counter_get_value (counter));
+#endif
+  
+  /* disable or re-enable the read watch for the transport if
+   * required.
+   */
+  if (* transport->vtable->live_messages_changed)
+    (* transport->vtable->live_messages_changed) (transport);
+
+  DBUS_TRANSPORT_RELEASE_REF (transport);
+}
 
 /**
  * Initializes the base class members of DBusTransport.
@@ -83,11 +104,12 @@ _dbus_transport_init_base (DBusTransport             *transport,
 {
   DBusMessageLoader *loader;
   DBusAuth *auth;
+  DBusCounter *counter;
   
   loader = _dbus_message_loader_new ();
   if (loader == NULL)
     return FALSE;
-
+  
   if (server)
     auth = _dbus_auth_server_new ();
   else
@@ -97,21 +119,40 @@ _dbus_transport_init_base (DBusTransport             *transport,
       _dbus_message_loader_unref (loader);
       return FALSE;
     }
+
+  counter = _dbus_counter_new ();
+  if (counter == NULL)
+    {
+      _dbus_auth_unref (auth);
+      _dbus_message_loader_unref (loader);
+      return FALSE;
+    }
   
   transport->refcount = 1;
   transport->vtable = vtable;
   transport->loader = loader;
   transport->auth = auth;
+  transport->live_messages_size = counter;
   transport->authenticated = FALSE;
   transport->messages_need_sending = FALSE;
   transport->disconnected = FALSE;
   transport->send_credentials_pending = !server;
   transport->receive_credentials_pending = server;
   transport->is_server = server;
+
+  /* Try to default to something that won't totally hose the system,
+   * but doesn't impose too much of a limitation.
+   */
+  transport->max_live_messages_size = _DBUS_ONE_MEGABYTE * 63;
   
   transport->credentials.pid = -1;
   transport->credentials.uid = -1;
   transport->credentials.gid = -1;
+
+  _dbus_counter_set_notify (transport->live_messages_size,
+                            transport->max_live_messages_size,
+                            live_messages_size_notify,
+                            transport);
   
   return TRUE;
 }
@@ -127,9 +168,12 @@ _dbus_transport_finalize_base (DBusTransport *transport)
 {
   if (!transport->disconnected)
     _dbus_transport_disconnect (transport);
-
+  
   _dbus_message_loader_unref (transport->loader);
   _dbus_auth_unref (transport->auth);
+  _dbus_counter_set_notify (transport->live_messages_size,
+                            0, NULL, NULL);
+  _dbus_counter_unref (transport->live_messages_size);
 }
 
 /**
@@ -403,4 +447,59 @@ _dbus_transport_do_iteration (DBusTransport  *transport,
   DBUS_TRANSPORT_RELEASE_REF (transport);
 }
 
+/**
+ * See dbus_connection_set_max_message_size().
+ *
+ * @param transport the transport
+ * @param size the max size of a single message
+ */
+void
+_dbus_transport_set_max_message_size (DBusTransport  *transport,
+                                      long            size)
+{
+  _dbus_message_loader_set_max_message_size (transport->loader, size);
+}
+
+/**
+ * See dbus_connection_get_max_message_size().
+ *
+ * @param transport the transport
+ * @returns max message size
+ */
+long
+_dbus_transport_get_max_message_size (DBusTransport  *transport)
+{
+  return _dbus_message_loader_get_max_message_size (transport->loader);
+}
+
+/**
+ * See dbus_connection_set_max_live_messages_size().
+ *
+ * @param transport the transport
+ * @param size the max size of all incoming messages
+ */
+void
+_dbus_transport_set_max_live_messages_size (DBusTransport  *transport,
+                                            long            size)
+{
+  transport->max_live_messages_size = size;
+  _dbus_counter_set_notify (transport->live_messages_size,
+                            transport->max_live_messages_size,
+                            live_messages_size_notify,
+                            transport);
+}
+
+
+/**
+ * See dbus_connection_get_max_live_messages_size().
+ *
+ * @param transport the transport
+ * @returns max bytes for all live messages
+ */
+long
+_dbus_transport_get_max_live_messages_size (DBusTransport  *transport)
+{
+  return transport->max_live_messages_size;
+}
+
 /** @} */
index 47062b7..5e7de3e 100644 (file)
@@ -30,24 +30,29 @@ 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);
-dbus_bool_t    _dbus_transport_get_is_authenticated (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);
-
+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);
+dbus_bool_t    _dbus_transport_get_is_authenticated       (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);
+void           _dbus_transport_set_max_message_size       (DBusTransport  *transport,
+                                                           long            size);
+long           _dbus_transport_get_max_message_size       (DBusTransport  *transport);
+void           _dbus_transport_set_max_live_messages_size (DBusTransport  *transport,
+                                                           long            size);
+long           _dbus_transport_get_max_live_messages_size (DBusTransport  *transport);
 
 
 DBUS_END_DECLS;
index 99f97ff..27e10e7 100644 (file)
@@ -9,6 +9,9 @@ new_connection_callback (DBusServer     *server,
 {
   printf ("Got new connection\n");
 
+  dbus_connection_set_max_live_messages_size (new_connection,
+                                              10);
+  
   setup_connection (new_connection);
 }
 
index 02a1e97..71d9cdc 100644 (file)
@@ -146,7 +146,7 @@ check_messages (void)
         {
           DBusMessage *reply;
 
-          printf ("Received message %d, sending reply\n", count);
+          fprintf (stderr, "Received message %d, sending reply\n", count);
           
           reply = dbus_message_new ("org.freedesktop.DBus.Test", "org.freedesktop.DBus.Test");
           dbus_connection_send_message (connection,