/* -*- 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
*
*/
#include "dbus-internals.h"
#include "dbus-protocol.h"
+#include "dbus-test.h"
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#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.
/**
* 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 unsigned long pid;
+
/* things are written a bit oddly here so that
* in the non-verbose case we just have the one
* conditional and return immediately.
if (!initted)
{
verbose = _dbus_getenv ("DBUS_VERBOSE") != NULL;
+ pid = _dbus_getpid ();
initted = TRUE;
if (!verbose)
return;
}
+
+ fprintf (stderr, "%lu: ", pid);
va_start (args, format);
vfprintf (stderr, format, 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.
}
/**
- * Sets a file descriptor to be nonblocking.
- *
- * @param fd the file descriptor.
- * @param result address of result code.
- * @returns #TRUE on success.
+ * 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.
*/
-dbus_bool_t
-_dbus_set_fd_nonblocking (int fd,
- DBusResultCode *result)
+char**
+_dbus_dup_string_array (const char **array)
{
- int val;
+ int len;
+ int i;
+ char **copy;
+
+ if (array == NULL)
+ return NULL;
- val = fcntl (fd, F_GETFL, 0);
- if (val < 0)
+ for (len = 0; array[len] != NULL; ++len)
+ ;
+
+ copy = dbus_new0 (char*, len + 1);
+ if (copy == NULL)
+ return NULL;
+
+ i = 0;
+ while (i < len)
{
- 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;
+ copy[i] = _dbus_strdup (array[i]);
+ if (copy[i] == NULL)
+ {
+ dbus_free_string_array (copy);
+ return NULL;
+ }
+
+ ++i;
}
- 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 copy;
+}
- return FALSE;
+/**
+ * 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_string_array_contains (const char **array,
+ const char *str)
+{
+ int i;
+
+ i = 0;
+ while (array[i] != NULL)
+ {
+ if (strcmp (array[i], str) == 0)
+ return TRUE;
+ ++i;
}
- return TRUE;
+ return FALSE;
}
/**
{
switch (type)
{
+ 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 "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";
}
}
+#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 */
+
/** @} */