2003-10-14 Havoc Pennington <hp@redhat.com>
[platform/upstream/dbus.git] / dbus / dbus-internals.c
index e90703e..44f3ff4 100644 (file)
@@ -1,7 +1,7 @@
 /* -*- mode: C; c-file-style: "gnu" -*- */
 /* dbus-internals.c  random utility stuff (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
  * 
@@ -21,6 +21,8 @@
  *
  */
 #include "dbus-internals.h"
+#include "dbus-protocol.h"
+#include "dbus-test.h"
 #include <stdio.h>
 #include <stdarg.h>
 #include <string.h>
@@ -28,6 +30,7 @@
 #include <errno.h>
 #include <unistd.h>
 #include <fcntl.h>
+#include <stdlib.h>
 
 /**
  * @defgroup DBusInternals D-BUS internal implementation details
  *
  * 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
  */
 
 /**
+ * @def _DBUS_LOCK_NAME
+ *
+ * Expands to name of a global lock variable.
+ */
+
+/**
+ * @def _DBUS_DEFINE_GLOBAL_LOCK
+ *
+ * Defines a global lock variable with the given name.
+ * The lock must be added to the list to initialize
+ * in dbus_threads_init().
+ */
+
+/**
+ * @def _DBUS_DECLARE_GLOBAL_LOCK
+ *
+ * Expands to declaration of a global lock defined
+ * with _DBUS_DEFINE_GLOBAL_LOCK.
+ * The lock must be added to the list to initialize
+ * in dbus_threads_init().
+ */
+
+/**
+ * @def _DBUS_LOCK
+ *
+ * Locks a global lock
+ */
+
+/**
+ * @def _DBUS_UNLOCK
+ *
+ * Unlocks a global lock
+ */
+
+/**
+ * Fixed "out of memory" error message, just to avoid
+ * making up a different string every time and wasting
+ * space.
+ */
+const char _dbus_no_memory_message[] = "Not enough memory";
+
+/**
  * Prints a warning message to stderr.
  *
  * @param format printf-style format string.
@@ -141,27 +174,50 @@ _dbus_warn (const char *format,
   va_end (args);
 }
 
+static dbus_bool_t verbose_initted = FALSE;
+
 /**
  * Prints a warning message to stderr
  * if the user has enabled verbose mode.
+ * This is the real function implementation,
+ * use _dbus_verbose() macro in code.
  *
  * @param format printf-style format string.
  */
 void
-_dbus_verbose (const char *format,
-               ...)
+_dbus_verbose_real (const char *format,
+                    ...)
 {
   va_list args;
   static dbus_bool_t verbose = TRUE;
-  static dbus_bool_t initted = FALSE;
-
+  static dbus_bool_t need_pid = TRUE;
+  
+  /* things are written a bit oddly here so that
+   * in the non-verbose case we just have the one
+   * conditional and return immediately.
+   */
   if (!verbose)
     return;
   
-  if (!initted)
+  if (!verbose_initted)
     {
-      verbose = getenv ("DBUS_VERBOSE") != NULL;
-      initted = TRUE;
+      verbose = _dbus_getenv ("DBUS_VERBOSE") != NULL;
+      verbose_initted = TRUE;
+      if (!verbose)
+        return;
+    }
+
+  if (need_pid)
+    {
+      int len;
+      
+      fprintf (stderr, "%lu: ", _dbus_getpid ());
+
+      len = strlen (format);
+      if (format[len-1] == '\n')
+        need_pid = TRUE;
+      else
+        need_pid = FALSE;
     }
   
   va_start (args, format);
@@ -170,103 +226,15 @@ _dbus_verbose (const char *format,
 }
 
 /**
- * A wrapper around strerror() because some platforms
- * may be lame and not have strerror().
+ * Reinitializes the verbose logging code, used
+ * as a hack in dbus-spawn.c so that a child
+ * process re-reads its pid
  *
- * @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)
+void
+_dbus_verbose_reset_real (void)
 {
-  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;
+  verbose_initted = FALSE;
 }
 
 /**
@@ -280,7 +248,7 @@ _dbus_result_from_errno (int error_number)
 char*
 _dbus_strdup (const char *str)
 {
-  int len;
+  size_t len;
   char *copy;
   
   if (str == NULL)
@@ -298,37 +266,298 @@ _dbus_strdup (const char *str)
 }
 
 /**
- * Sets a file descriptor to be nonblocking.
+ * Duplicates a block of memory. Returns
+ * #NULL on failure.
  *
- * @param fd the file descriptor.
- * @param result address of result code.
- * @returns #TRUE on success.
+ * @param mem memory to copy
+ * @param n_bytes number of bytes to copy
+ * @returns the copy
+ */
+void*
+_dbus_memdup (const void  *mem,
+              size_t       n_bytes)
+{
+  void *copy;
+
+  copy = dbus_malloc (n_bytes);
+  if (copy == NULL)
+    return NULL;
+
+  memcpy (copy, mem, n_bytes);
+  
+  return copy;
+}
+
+/**
+ * Duplicates a string array. Result may be freed with
+ * dbus_free_string_array(). Returns #NULL if memory allocation fails.
+ * If the array to be duplicated is #NULL, returns #NULL.
+ * 
+ * @param array array to duplicate.
+ * @returns newly-allocated copy.
+ */
+char**
+_dbus_dup_string_array (const char **array)
+{
+  int len;
+  int i;
+  char **copy;
+  
+  if (array == NULL)
+    return NULL;
+
+  for (len = 0; array[len] != NULL; ++len)
+    ;
+
+  copy = dbus_new0 (char*, len + 1);
+  if (copy == NULL)
+    return NULL;
+
+  i = 0;
+  while (i < len)
+    {
+      copy[i] = _dbus_strdup (array[i]);
+      if (copy[i] == NULL)
+        {
+          dbus_free_string_array (copy);
+          return NULL;
+        }
+
+      ++i;
+    }
+
+  return copy;
+}
+
+/**
+ * Checks whether a string array contains the given string.
+ * 
+ * @param array array to search.
+ * @param str string to look for
+ * @returns #TRUE if array contains string
  */
 dbus_bool_t
-_dbus_set_fd_nonblocking (int             fd,
-                          DBusResultCode *result)
+_dbus_string_array_contains (const char **array,
+                             const char  *str)
 {
-  int val;
+  int i;
 
-  val = fcntl (fd, F_GETFL, 0);
-  if (val < 0)
+  i = 0;
+  while (array[i] != NULL)
     {
-      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 (strcmp (array[i], str) == 0)
+        return TRUE;
+      ++i;
     }
 
-  if (fcntl (fd, F_SETFL, val | O_NONBLOCK) < 0)
+  return FALSE;
+}
+
+/**
+ * Returns a string describing the given type.
+ *
+ * @param type the type to describe
+ * @returns a constant string describing the type
+ */
+const char *
+_dbus_type_to_string (int type)
+{
+  switch (type)
     {
-      dbus_set_result (result, _dbus_result_from_errno (errno));      
-      _dbus_verbose ("Failed to set fd %d nonblocking: %s\n",
-                     fd, _dbus_strerror (errno));
+    case DBUS_TYPE_INVALID:
+      return "invalid";
+    case DBUS_TYPE_NIL:
+      return "nil";
+    case DBUS_TYPE_BOOLEAN:
+      return "boolean";
+    case DBUS_TYPE_INT32:
+      return "int32";
+    case DBUS_TYPE_UINT32:
+      return "uint32";
+    case DBUS_TYPE_DOUBLE:
+      return "double";
+    case DBUS_TYPE_STRING:
+      return "string";
+    case DBUS_TYPE_NAMED:
+      return "named";
+    case DBUS_TYPE_ARRAY:
+      return "array";
+    case DBUS_TYPE_DICT:
+      return "dict";
+    default:
+      return "unknown";
+    }
+}
 
-      return FALSE;
+/**
+ * Returns a string describing the given name.
+ *
+ * @param header_field the field to describe
+ * @returns a constant string describing the field
+ */
+const char *
+_dbus_header_field_to_string (int header_field)
+{
+  switch (header_field)
+    {
+    case DBUS_HEADER_FIELD_INVALID:
+      return "invalid";
+    case DBUS_HEADER_FIELD_PATH:
+      return "path";
+    case DBUS_HEADER_FIELD_INTERFACE:
+      return "interface";
+    case DBUS_HEADER_FIELD_MEMBER:
+      return "member";
+    case DBUS_HEADER_FIELD_ERROR_NAME:
+      return "error-name";
+    case DBUS_HEADER_FIELD_REPLY_SERIAL:
+      return "reply-serial";
+    case DBUS_HEADER_FIELD_SERVICE:
+      return "service";
+    case DBUS_HEADER_FIELD_SENDER_SERVICE:
+      return "sender-service";
+    default:
+      return "unknown";
     }
+}
+
+#ifndef DBUS_DISABLE_CHECKS
+/** String used in _dbus_return_if_fail macro */
+const char _dbus_return_if_fail_warning_format[] =
+"Arguments to %s() were incorrect, assertion \"%s\" failed in file %s line %d.\n"
+"This is normally a bug in some application using the D-BUS library.\n";
+#endif
+
+#ifndef DBUS_DISABLE_ASSERT
+/**
+ * Internals of _dbus_assert(); it's a function
+ * rather than a macro with the inline code so
+ * that the assertion failure blocks don't show up
+ * in test suite coverage, and to shrink code size.
+ *
+ * @param condition TRUE if assertion succeeded
+ * @param condition_text condition as a string
+ * @param file file the assertion is in
+ * @param line line the assertion is in
+ */
+void
+_dbus_real_assert (dbus_bool_t  condition,
+                   const char  *condition_text,
+                   const char  *file,
+                   int          line)
+{
+  if (!condition)
+    {
+      _dbus_warn ("Assertion failed \"%s\" file \"%s\" line %d process %lu\n",
+                  condition_text, file, line, _dbus_getpid ());
+      _dbus_abort ();
+    }
+}
+
+/**
+ * Internals of _dbus_assert_not_reached(); it's a function
+ * rather than a macro with the inline code so
+ * that the assertion failure blocks don't show up
+ * in test suite coverage, and to shrink code size.
+ *
+ * @param explanation what was reached that shouldn't have been
+ * @param file file the assertion is in
+ * @param line line the assertion is in
+ */
+void
+_dbus_real_assert_not_reached (const char *explanation,
+                               const char *file,
+                               int         line)
+{
+  _dbus_warn ("File \"%s\" line %d process %lu should not have been reached: %s\n",
+              file, line, _dbus_getpid (), explanation);
+  _dbus_abort ();
+}
+#endif /* DBUS_DISABLE_ASSERT */
+  
+#ifdef DBUS_BUILD_TESTS
+static dbus_bool_t
+run_failing_each_malloc (int                    n_mallocs,
+                         const char            *description,
+                         DBusTestMemoryFunction func,
+                         void                  *data)
+{
+  n_mallocs += 10; /* fudge factor to ensure reallocs etc. are covered */
+  
+  while (n_mallocs >= 0)
+    {      
+      _dbus_set_fail_alloc_counter (n_mallocs);
+
+      _dbus_verbose ("\n===\n%s: (will fail malloc %d with %d failures)\n===\n",
+                     description, n_mallocs,
+                     _dbus_get_fail_alloc_failures ());
+
+      if (!(* func) (data))
+        return FALSE;
+      
+      n_mallocs -= 1;
+    }
+
+  _dbus_set_fail_alloc_counter (_DBUS_INT_MAX);
+
+  return TRUE;
+}                        
+
+/**
+ * Tests how well the given function responds to out-of-memory
+ * situations. Calls the function repeatedly, failing a different
+ * call to malloc() each time. If the function ever returns #FALSE,
+ * the test fails. The function should return #TRUE whenever something
+ * valid (such as returning an error, or succeeding) occurs, and #FALSE
+ * if it gets confused in some way.
+ *
+ * @param description description of the test used in verbose output
+ * @param func function to call
+ * @param data data to pass to function
+ * @returns #TRUE if the function never returns FALSE
+ */
+dbus_bool_t
+_dbus_test_oom_handling (const char             *description,
+                         DBusTestMemoryFunction  func,
+                         void                   *data)
+{
+  int approx_mallocs;
+
+  /* Run once to see about how many mallocs are involved */
+  
+  _dbus_set_fail_alloc_counter (_DBUS_INT_MAX);
+
+  _dbus_verbose ("Running once to count mallocs\n");
+  
+  if (!(* func) (data))
+    return FALSE;
+  
+  approx_mallocs = _DBUS_INT_MAX - _dbus_get_fail_alloc_counter ();
+
+  _dbus_verbose ("\n=================\n%s: about %d mallocs total\n=================\n",
+                 description, approx_mallocs);
+
+  _dbus_set_fail_alloc_failures (1);
+  if (!run_failing_each_malloc (approx_mallocs, description, func, data))
+    return FALSE;
+
+  _dbus_set_fail_alloc_failures (2);
+  if (!run_failing_each_malloc (approx_mallocs, description, func, data))
+    return FALSE;
+  
+  _dbus_set_fail_alloc_failures (3);
+  if (!run_failing_each_malloc (approx_mallocs, description, func, data))
+    return FALSE;
+
+  _dbus_set_fail_alloc_failures (4);
+  if (!run_failing_each_malloc (approx_mallocs, description, func, data))
+    return FALSE;
+  
+  _dbus_verbose ("\n=================\n%s: all iterations passed\n=================\n",
+                 description);
 
   return TRUE;
 }
+#endif /* DBUS_BUILD_TESTS */
 
 /** @} */