-/* -*- mode: C; c-file-style: "gnu" -*- */
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/* dbus-internals.c random utility stuff (internal to D-Bus implementation)
*
* Copyright (C) 2002, 2003 Red Hat, Inc.
*
* 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
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
+
+#include <config.h>
#include "dbus-internals.h"
#include "dbus-protocol.h"
+#include "dbus-marshal-basic.h"
#include "dbus-test.h"
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <stdlib.h>
+#ifdef DBUS_USE_OUTPUT_DEBUG_STRING
+#include <windows.h>
+#include <mbstring.h>
+#endif
/**
- * @defgroup DBusInternals D-Bus internal implementation details
+ * @defgroup DBusInternals D-Bus secret internal implementation details
* @brief Documentation useful when developing or debugging D-Bus itself.
*
*/
* making up a different string every time and wasting
* space.
*/
-const char _dbus_no_memory_message[] = "Not enough memory";
+const char *_dbus_no_memory_message = "Not enough memory";
+
+static dbus_bool_t warn_initted = FALSE;
+static dbus_bool_t fatal_warnings = FALSE;
+static dbus_bool_t fatal_warnings_on_check_failed = TRUE;
+
+static void
+init_warnings(void)
+{
+ if (!warn_initted)
+ {
+ const char *s;
+ s = _dbus_getenv ("DBUS_FATAL_WARNINGS");
+ if (s && *s)
+ {
+ if (*s == '0')
+ {
+ fatal_warnings = FALSE;
+ fatal_warnings_on_check_failed = FALSE;
+ }
+ else if (*s == '1')
+ {
+ fatal_warnings = TRUE;
+ fatal_warnings_on_check_failed = TRUE;
+ }
+ else
+ {
+ fprintf(stderr, "DBUS_FATAL_WARNINGS should be set to 0 or 1 if set, not '%s'",
+ s);
+ }
+ }
+
+ warn_initted = TRUE;
+ }
+}
/**
- * Prints a warning message to stderr.
+ * Prints a warning message to stderr. Can optionally be made to exit
+ * fatally by setting DBUS_FATAL_WARNINGS, but this is rarely
+ * used. This function should be considered pretty much equivalent to
+ * fprintf(stderr). _dbus_warn_check_failed() on the other hand is
+ * suitable for use when a programming mistake has been made.
*
* @param format printf-style format string.
*/
_dbus_warn (const char *format,
...)
{
- /* FIXME not portable enough? */
va_list args;
+ if (!warn_initted)
+ init_warnings ();
+
va_start (args, format);
vfprintf (stderr, format, args);
va_end (args);
+
+ if (fatal_warnings)
+ {
+ fflush (stderr);
+ _dbus_abort ();
+ }
+}
+
+/**
+ * Prints a "critical" warning to stderr when an assertion fails;
+ * differs from _dbus_warn primarily in that it prefixes the pid and
+ * defaults to fatal. This should be used only when a programming
+ * error has been detected. (NOT for unavoidable errors that an app
+ * might handle - those should be returned as DBusError.) Calling this
+ * means "there is a bug"
+ */
+void
+_dbus_warn_check_failed(const char *format,
+ ...)
+{
+ va_list args;
+
+ if (!warn_initted)
+ init_warnings ();
+
+ fprintf (stderr, "process %lu: ", _dbus_pid_for_log ());
+
+ va_start (args, format);
+ vfprintf (stderr, format, args);
+ va_end (args);
+
+ if (fatal_warnings_on_check_failed)
+ {
+ fflush (stderr);
+ _dbus_abort ();
+ }
}
#ifdef DBUS_ENABLE_VERBOSE_MODE
static dbus_bool_t verbose_initted = FALSE;
static dbus_bool_t verbose = TRUE;
+/** Whether to show the current thread in verbose messages */
#define PTHREAD_IN_VERBOSE 0
#if PTHREAD_IN_VERBOSE
#include <pthread.h>
#endif
+#ifdef _MSC_VER
+#define inline
+#endif
+#ifdef DBUS_USE_OUTPUT_DEBUG_STRING
+static char module_name[1024];
+#endif
+
static inline void
_dbus_verbose_init (void)
{
if (!verbose_initted)
{
- const char *p = _dbus_getenv ("DBUS_VERBOSE");
+ const char *p = _dbus_getenv ("DBUS_VERBOSE");
verbose = p != NULL && *p == '1';
verbose_initted = TRUE;
+#ifdef DBUS_USE_OUTPUT_DEBUG_STRING
+ {
+ char *last_period, *last_slash;
+ GetModuleFileName(0,module_name,sizeof(module_name)-1);
+ last_period = _mbsrchr(module_name,'.');
+ if (last_period)
+ *last_period ='\0';
+ last_slash = _mbsrchr(module_name,'\\');
+ if (last_slash)
+ strcpy(module_name,last_slash+1);
+ strcat(module_name,": ");
+ }
+#endif
}
}
+/** @def DBUS_IS_DIR_SEPARATOR(c)
+ * macro for checking if character c is a patch separator
+ *
+ * @todo move to a header file so that others can use this too
+ */
+#ifdef DBUS_WIN
+#define DBUS_IS_DIR_SEPARATOR(c) (c == '\\' || c == '/')
+#else
+#define DBUS_IS_DIR_SEPARATOR(c) (c == '/')
+#endif
+
+/**
+ remove source root from file path
+ the source root is determined by
+*/
+static char *_dbus_file_path_extract_elements_from_tail(const char *file,int level)
+{
+ static int prefix = -1;
+ char *p;
+
+ if (prefix == -1)
+ {
+ char *p = (char *)file + strlen(file);
+ int i = 0;
+ prefix = 0;
+ for (;p >= file;p--)
+ {
+ if (DBUS_IS_DIR_SEPARATOR(*p))
+ {
+ if (++i >= level)
+ {
+ prefix = p-file+1;
+ break;
+ }
+ }
+ }
+ }
+ return (char *)file+prefix;
+}
+
+/**
+ * Implementation of dbus_is_verbose() macro if built with verbose logging
+ * enabled.
+ * @returns whether verbose logging is active.
+ */
dbus_bool_t
_dbus_is_verbose_real (void)
{
* @param format printf-style format string.
*/
void
+#ifdef DBUS_CPP_SUPPORTS_VARIABLE_MACRO_ARGUMENTS
+_dbus_verbose_real (const char *file,
+ const int line,
+ const char *function,
+ const char *format,
+#else
_dbus_verbose_real (const char *format,
+#endif
...)
{
va_list args;
if (!_dbus_is_verbose_real())
return;
+#ifndef DBUS_USE_OUTPUT_DEBUG_STRING
/* Print out pid before the line */
if (need_pid)
{
#if PTHREAD_IN_VERBOSE
- fprintf (stderr, "%lu: 0x%lx: ", _dbus_getpid (), pthread_self ());
+ fprintf (stderr, "%lu: 0x%lx: ", _dbus_pid_for_log (), pthread_self ());
#else
- fprintf (stderr, "%lu: ", _dbus_getpid ());
+ fprintf (stderr, "%lu: ", _dbus_pid_for_log ());
#endif
}
-
+#endif
/* Only print pid again if the next line is a new line */
len = strlen (format);
need_pid = TRUE;
else
need_pid = FALSE;
-
+
va_start (args, format);
+#ifdef DBUS_USE_OUTPUT_DEBUG_STRING
+ {
+ char buf[1024];
+ strcpy(buf,module_name);
+#ifdef DBUS_CPP_SUPPORTS_VARIABLE_MACRO_ARGUMENTS
+ sprintf (buf+strlen(buf), "[%s(%d):%s] ",_dbus_file_path_extract_elements_from_tail(file,2),line,function);
+#endif
+ vsprintf (buf+strlen(buf),format, args);
+ va_end (args);
+ OutputDebugString(buf);
+ }
+#else
+#ifdef DBUS_CPP_SUPPORTS_VARIABLE_MACRO_ARGUMENTS
+ fprintf (stderr, "[%s(%d):%s] ",_dbus_file_path_extract_elements_from_tail(file,2),line,function);
+#endif
+
vfprintf (stderr, format, args);
va_end (args);
fflush (stderr);
+#endif
}
/**
return FALSE;
}
+/**
+ * Generates a new UUID. If you change how this is done,
+ * there's some text about it in the spec that should also change.
+ *
+ * @param uuid the uuid to initialize
+ */
+void
+_dbus_generate_uuid (DBusGUID *uuid)
+{
+ long now;
+
+ _dbus_get_current_time (&now, NULL);
+
+ uuid->as_uint32s[DBUS_UUID_LENGTH_WORDS - 1] = DBUS_UINT32_TO_BE (now);
+
+ _dbus_generate_random_bytes_buffer (uuid->as_bytes, DBUS_UUID_LENGTH_BYTES - 4);
+}
+
+/**
+ * Hex-encode a UUID.
+ *
+ * @param uuid the uuid
+ * @param encoded string to append hex uuid to
+ * @returns #FALSE if no memory
+ */
+dbus_bool_t
+_dbus_uuid_encode (const DBusGUID *uuid,
+ DBusString *encoded)
+{
+ DBusString binary;
+ _dbus_string_init_const_len (&binary, uuid->as_bytes, DBUS_UUID_LENGTH_BYTES);
+ return _dbus_string_hex_encode (&binary, 0, encoded, _dbus_string_get_length (encoded));
+}
+
+static dbus_bool_t
+_dbus_read_uuid_file_without_creating (const DBusString *filename,
+ DBusGUID *uuid,
+ DBusError *error)
+{
+ DBusString contents;
+ DBusString decoded;
+ int end;
+
+ if (!_dbus_string_init (&contents))
+ {
+ _DBUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ if (!_dbus_string_init (&decoded))
+ {
+ _dbus_string_free (&contents);
+ _DBUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ if (!_dbus_file_get_contents (&contents, filename, error))
+ goto error;
+
+ _dbus_string_chop_white (&contents);
+
+ if (_dbus_string_get_length (&contents) != DBUS_UUID_LENGTH_HEX)
+ {
+ dbus_set_error (error, DBUS_ERROR_INVALID_FILE_CONTENT,
+ "UUID file '%s' should contain a hex string of length %d, not length %d, with no other text",
+ _dbus_string_get_const_data (filename),
+ DBUS_UUID_LENGTH_HEX,
+ _dbus_string_get_length (&contents));
+ goto error;
+ }
+
+ if (!_dbus_string_hex_decode (&contents, 0, &end, &decoded, 0))
+ {
+ _DBUS_SET_OOM (error);
+ goto error;
+ }
+
+ if (end == 0)
+ {
+ dbus_set_error (error, DBUS_ERROR_INVALID_FILE_CONTENT,
+ "UUID file '%s' contains invalid hex data",
+ _dbus_string_get_const_data (filename));
+ goto error;
+ }
+
+ if (_dbus_string_get_length (&decoded) != DBUS_UUID_LENGTH_BYTES)
+ {
+ dbus_set_error (error, DBUS_ERROR_INVALID_FILE_CONTENT,
+ "UUID file '%s' contains %d bytes of hex-encoded data instead of %d",
+ _dbus_string_get_const_data (filename),
+ _dbus_string_get_length (&decoded),
+ DBUS_UUID_LENGTH_BYTES);
+ goto error;
+ }
+
+ _dbus_string_copy_to_buffer (&decoded, uuid->as_bytes, DBUS_UUID_LENGTH_BYTES);
+
+ _dbus_string_free (&decoded);
+ _dbus_string_free (&contents);
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ return TRUE;
+
+ error:
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+ _dbus_string_free (&contents);
+ _dbus_string_free (&decoded);
+ return FALSE;
+}
+
+static dbus_bool_t
+_dbus_create_uuid_file_exclusively (const DBusString *filename,
+ DBusGUID *uuid,
+ DBusError *error)
+{
+ DBusString encoded;
+
+ if (!_dbus_string_init (&encoded))
+ {
+ _DBUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ _dbus_generate_uuid (uuid);
+
+ if (!_dbus_uuid_encode (uuid, &encoded))
+ {
+ _DBUS_SET_OOM (error);
+ goto error;
+ }
+
+ /* FIXME this is racy; we need a save_file_exclusively
+ * function. But in practice this should be fine for now.
+ *
+ * - first be sure we can create the file and it
+ * doesn't exist by creating it empty with O_EXCL
+ * - then create it by creating a temporary file and
+ * overwriting atomically with rename()
+ */
+ if (!_dbus_create_file_exclusively (filename, error))
+ goto error;
+
+ if (!_dbus_string_append_byte (&encoded, '\n'))
+ {
+ _DBUS_SET_OOM (error);
+ goto error;
+ }
+
+ if (!_dbus_string_save_to_file (&encoded, filename, error))
+ goto error;
+
+ if (!_dbus_make_file_world_readable (filename, error))
+ goto error;
+
+ _dbus_string_free (&encoded);
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+ return TRUE;
+
+ error:
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+ _dbus_string_free (&encoded);
+ return FALSE;
+}
+
+/**
+ * Reads (and optionally writes) a uuid to a file. Initializes the uuid
+ * unless an error is returned.
+ *
+ * @param filename the name of the file
+ * @param uuid uuid to be initialized with the loaded uuid
+ * @param create_if_not_found #TRUE to create a new uuid and save it if the file doesn't exist
+ * @param error the error return
+ * @returns #FALSE if the error is set
+ */
+dbus_bool_t
+_dbus_read_uuid_file (const DBusString *filename,
+ DBusGUID *uuid,
+ dbus_bool_t create_if_not_found,
+ DBusError *error)
+{
+ DBusError read_error = DBUS_ERROR_INIT;
+
+ if (_dbus_read_uuid_file_without_creating (filename, uuid, &read_error))
+ return TRUE;
+
+ if (!create_if_not_found)
+ {
+ dbus_move_error (&read_error, error);
+ return FALSE;
+ }
+
+ /* If the file exists and contains junk, we want to keep that error
+ * message instead of overwriting it with a "file exists" error
+ * message when we try to write
+ */
+ if (dbus_error_has_name (&read_error, DBUS_ERROR_INVALID_FILE_CONTENT))
+ {
+ dbus_move_error (&read_error, error);
+ return FALSE;
+ }
+ else
+ {
+ dbus_error_free (&read_error);
+ return _dbus_create_uuid_file_exclusively (filename, uuid, error);
+ }
+}
+
+_DBUS_DEFINE_GLOBAL_LOCK (machine_uuid);
+static int machine_uuid_initialized_generation = 0;
+static DBusGUID machine_uuid;
+
+/**
+ * Gets the hex-encoded UUID of the machine this function is
+ * executed on. This UUID is guaranteed to be the same for a given
+ * machine at least until it next reboots, though it also
+ * makes some effort to be the same forever, it may change if the
+ * machine is reconfigured or its hardware is modified.
+ *
+ * @param uuid_str string to append hex-encoded machine uuid to
+ * @returns #FALSE if no memory
+ */
+dbus_bool_t
+_dbus_get_local_machine_uuid_encoded (DBusString *uuid_str)
+{
+ dbus_bool_t ok;
+
+ _DBUS_LOCK (machine_uuid);
+ if (machine_uuid_initialized_generation != _dbus_current_generation)
+ {
+ DBusError error = DBUS_ERROR_INIT;
+
+ if (!_dbus_read_local_machine_uuid (&machine_uuid, FALSE,
+ &error))
+ {
+#ifndef DBUS_BUILD_TESTS
+ /* For the test suite, we may not be installed so just continue silently
+ * here. But in a production build, we want to be nice and loud about
+ * this.
+ */
+ _dbus_warn_check_failed ("D-Bus library appears to be incorrectly set up; failed to read machine uuid: %s\n"
+ "See the manual page for dbus-uuidgen to correct this issue.\n",
+ error.message);
+#endif
+
+ dbus_error_free (&error);
+
+ _dbus_generate_uuid (&machine_uuid);
+ }
+ }
+
+ ok = _dbus_uuid_encode (&machine_uuid, uuid_str);
+
+ _DBUS_UNLOCK (machine_uuid);
+
+ return ok;
+}
+
#ifdef DBUS_BUILD_TESTS
/**
* Returns a string describing the given name.
#ifndef DBUS_DISABLE_CHECKS
/** String used in _dbus_return_if_fail macro */
-const char _dbus_return_if_fail_warning_format[] =
-"%lu: arguments to %s() were incorrect, assertion \"%s\" failed in file %s line %d.\n"
+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
if (_DBUS_UNLIKELY (!condition))
{
_dbus_warn ("%lu: assertion failed \"%s\" file \"%s\" line %d function %s\n",
- _dbus_getpid (), condition_text, file, line, func);
+ _dbus_pid_for_log (), condition_text, file, line, func);
_dbus_abort ();
}
}
int line)
{
_dbus_warn ("File \"%s\" line %d process %lu should not have been reached: %s\n",
- file, line, _dbus_getpid (), explanation);
+ file, line, _dbus_pid_for_log (), explanation);
_dbus_abort ();
}
#endif /* DBUS_DISABLE_ASSERT */