set handlers patch
Havoc Pennington
hp at redhat.com
Fri Nov 24 18:10:22 PST 2006
Havoc Pennington wrote:
> Attached is an API patch (you'll notice the implementation is empty) to
> add dbus_set_log_handler() and dbus_set_bug_handler().
>
> The primary purpose of dbus_set_log_handler() is to allow the bus daemon
> to use syslog() instead of printing to stderr.
>
> The primary purpose of dbus_set_bug_handler() is to allow apps with
> special needs to hook into failed checks and control the behavior to the
> extent possible.
>
Attached is a patch with slightly changed API and an implementation. The
implementation inherits some cruft from the current
_dbus_warn/_dbus_verbose implementations, might clean that up in an
additional patch after putting this one in.
Havoc
-------------- next part --------------
Index: ChangeLog
===================================================================
RCS file: /cvs/dbus/dbus/ChangeLog,v
retrieving revision 1.1206
diff -u -p -r1.1206 ChangeLog
--- ChangeLog 15 Nov 2006 03:07:59 -0000 1.1206
+++ ChangeLog 25 Nov 2006 01:59:24 -0000
@@ -1,3 +1,9 @@
+2006-11-24 Havoc Pennington <hp at redhat.com>
+
+ * dbus/dbus-misc.c: Add new API dbus_set_bug_handler(),
+ dbus_set_log_handler(). Move the implementations of _dbus_warn,
+ dbus_warn_check_failed, and _dbus_verbose into this file.
+
2006-11-14 Havoc Pennington <hp at redhat.com>
* dbus/dbus-misc.c, dbus/dbus-misc.h: Move
Index: dbus/dbus-errors.c
===================================================================
RCS file: /cvs/dbus/dbus/dbus/dbus-errors.c,v
retrieving revision 1.32
diff -u -p -r1.32 dbus-errors.c
--- dbus/dbus-errors.c 21 Oct 2006 17:08:08 -0000 1.32
+++ dbus/dbus-errors.c 25 Nov 2006 01:59:24 -0000
@@ -145,7 +145,27 @@ message_from_error (const char *error)
* warnings, exit on a failed assertion, or even crash in those cases
* (in other words, incorrect use of the API results in undefined
* behavior, possibly accompanied by helpful debugging output if
- * you're lucky).
+ * you're lucky). See dbus_set_bug_handler() for more details.
+ *
+ * Functions have a #DBusError parameter only if they can report
+ * multiple different errors that need to be distinguished. Some
+ * functions can report zero errors (they always succeed), while
+ * others have only one possible error (usually "insufficient
+ * memory"). If a function has only one possible error it may simply
+ * return #FALSE or #NULL to indicate that the error occurred, and not
+ * bother with #DBusError. See the documentation for each specific
+ * function for more.
+ *
+ * #DBusError may be set from a message of type
+ * #DBUS_MESSAGE_TYPE_ERROR received from another application. In
+ * these cases, you should not make any assumptions about the
+ * content of error names and messages, other than those guaranteed
+ * by the D-Bus protocol (for example, the protocol guarantees
+ * valid UTF-8 and sane message length). The remote app can provide
+ * any error name and message it wants, though, and libdbus has
+ * no way to validate it.
+ * dbus_connection_send_with_reply_and_block() is one example
+ * of a function that gets its error from a remote application.
*
* @{
*/
Index: dbus/dbus-internals.c
===================================================================
RCS file: /cvs/dbus/dbus/dbus/dbus-internals.c,v
retrieving revision 1.55
diff -u -p -r1.55 dbus-internals.c
--- dbus/dbus-internals.c 15 Nov 2006 01:52:01 -0000 1.55
+++ dbus/dbus-internals.c 25 Nov 2006 01:59:24 -0000
@@ -191,195 +191,6 @@
*/
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. 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.
- */
-void
-_dbus_warn (const char *format,
- ...)
-{
- 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_getpid ());
-
- 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
-
-static inline void
-_dbus_verbose_init (void)
-{
- if (!verbose_initted)
- {
- const char *p = _dbus_getenv ("DBUS_VERBOSE");
- verbose = p != NULL && *p == '1';
- verbose_initted = TRUE;
- }
-}
-
-/**
- * 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)
-{
- _dbus_verbose_init ();
- return verbose;
-}
-
-/**
- * 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_real (const char *format,
- ...)
-{
- va_list args;
- static dbus_bool_t need_pid = TRUE;
- int len;
-
- /* things are written a bit oddly here so that
- * in the non-verbose case we just have the one
- * conditional and return immediately.
- */
- if (!_dbus_is_verbose_real())
- return;
-
- /* Print out pid before the line */
- if (need_pid)
- {
-#if PTHREAD_IN_VERBOSE
- fprintf (stderr, "%lu: 0x%lx: ", _dbus_getpid (), pthread_self ());
-#else
- fprintf (stderr, "%lu: ", _dbus_getpid ());
-#endif
- }
-
-
- /* Only print pid again if the next line is a new line */
- len = strlen (format);
- if (format[len-1] == '\n')
- need_pid = TRUE;
- else
- need_pid = FALSE;
-
- va_start (args, format);
- vfprintf (stderr, format, args);
- va_end (args);
-
- fflush (stderr);
-}
-
-/**
- * Reinitializes the verbose logging code, used
- * as a hack in dbus-spawn.c so that a child
- * process re-reads its pid
- *
- */
-void
-_dbus_verbose_reset_real (void)
-{
- verbose_initted = FALSE;
-}
-
-#endif /* DBUS_ENABLE_VERBOSE_MODE */
-
/**
* Duplicates a string. Result must be freed with
* dbus_free(). Returns #NULL if memory allocation fails.
Index: dbus/dbus-internals.h
===================================================================
RCS file: /cvs/dbus/dbus/dbus/dbus-internals.h,v
retrieving revision 1.65
diff -u -p -r1.65 dbus-internals.h
--- dbus/dbus-internals.h 15 Nov 2006 01:52:01 -0000 1.65
+++ dbus/dbus-internals.h 25 Nov 2006 01:59:25 -0000
@@ -45,7 +45,6 @@ void _dbus_warn (const cha
void _dbus_warn_check_failed (const char *format,
...) _DBUS_GNUC_PRINTF (1, 2);
-
#if defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
#define _DBUS_FUNCTION_NAME __func__
#elif defined(__GNUC__)
Index: dbus/dbus-misc.c
===================================================================
RCS file: /cvs/dbus/dbus/dbus/dbus-misc.c,v
retrieving revision 1.2
diff -u -p -r1.2 dbus-misc.c
--- dbus/dbus-misc.c 15 Nov 2006 03:07:59 -0000 1.2
+++ dbus/dbus-misc.c 25 Nov 2006 01:59:25 -0000
@@ -4,7 +4,7 @@
* Copyright (C) 2006 Red Hat, Inc.
*
* Licensed under the Academic Free License version 2.1
- *
+ *
* 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
@@ -14,7 +14,7 @@
* 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
@@ -25,6 +25,422 @@
#include "dbus-misc.h"
#include "dbus-internals.h"
#include "dbus-string.h"
+#include <stdio.h>
+#include <stdarg.h>
+
+/**
+ * @defgroup DBusMiscInternals Internal of miscellaneous API
+ * @ingroup DBusInternals
+ * @brief Implementation details of dbus-misc.c
+ *@{
+ */
+
+typedef struct {
+ DBusLogHandlerFunction func; /**< the handler */
+ void *data; /**< handler data */
+ DBusFreeFunction free_data_func; /**< free handler data */
+} DBusLogHandler;
+
+
+static DBusLogHandler log_handler_bug = { NULL, NULL, NULL };
+static DBusLogHandler log_handler_warning = { NULL, NULL, NULL };
+static DBusLogHandler log_handler_verbose = { NULL, NULL, NULL };
+
+typedef struct {
+ DBusBugHandlerFunction func; /**< the handler */
+ void *data; /**< handler data */
+ DBusFreeFunction free_data_func; /**< free handler data */
+} DBusBugHandler;
+
+
+static DBusBugHandler bug_handler = { NULL, NULL, NULL };
+
+
+#if 0
+static dbus_bool_t _dbus_log (DBusLogLevel level,
+ const char *format,
+ ...) _DBUS_GNUC_PRINTF(2, 3);
+#endif
+
+/* the point of this is mostly to avoid confusion when debugging an OOM-handling problem with --verbose */
+static void
+log_oom_warning (const char *format)
+{
+ fputs ("No memory to print a D-Bus message with format string: ", stderr);
+ fputs (format, stderr);
+ fputs ("\n", stderr);
+ fflush (stderr);
+}
+
+static DBusLogHandler*
+get_handler (DBusLogLevel level)
+{
+ DBusLogHandler *handler = NULL;
+ switch (level)
+ {
+ case DBUS_LOG_LEVEL_BUG:
+ handler = &log_handler_bug;
+ break;
+ case DBUS_LOG_LEVEL_WARNING:
+ handler = &log_handler_warning;
+ break;
+ case DBUS_LOG_LEVEL_VERBOSE:
+ handler = &log_handler_verbose;
+ break;
+ }
+ return handler;
+}
+
+static void
+_dbus_log_literal (DBusLogLevel level,
+ const DBusString *str)
+{
+ DBusLogHandler *handler;
+
+ handler = get_handler (level);
+
+ if (handler->func)
+ {
+ (* handler->func) (_dbus_string_get_const_data (str), handler->data);
+ }
+ else
+ {
+ fputs (_dbus_string_get_const_data (str), stderr);
+ }
+}
+
+static dbus_bool_t
+_dbus_log_valist (DBusLogLevel level,
+ const char *format,
+ va_list args)
+{
+ DBusString str;
+
+ /* Unfortunately, this function could disastrously recurse if an
+ * assertion fails in any code it uses, but I'm not sure what to do
+ * about that short of cut-and-pasting everything. (Maybe we
+ * should.)
+ */
+
+ if (!_dbus_string_init (&str))
+ return FALSE;
+
+ if (!_dbus_string_append_printf_valist (&str, format, args))
+ {
+ _dbus_string_free (&str);
+ return FALSE;
+ }
+
+ _dbus_log_literal (level, &str);
+
+ _dbus_string_free (&str);
+
+ return TRUE;
+}
+
+#if 0
+static dbus_bool_t
+_dbus_log (DBusLogLevel level,
+ const char *format,
+ ...)
+{
+ va_list args;
+ dbus_bool_t ok;
+
+ va_start (args, format);
+ ok = _dbus_log_valist (level, format, args);
+ va_end (args);
+
+ return ok;
+}
+#endif
+
+static dbus_bool_t warn_initted = FALSE;
+static dbus_bool_t fatal_warnings = FALSE;
+static dbus_bool_t fatal_bugs = 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_bugs = FALSE;
+ }
+ else if (*s == '1')
+ {
+ fatal_warnings = TRUE;
+ fatal_bugs = 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. 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.
+ *
+ * dbus_set_log_handler() can be used to override what this function
+ * does.
+ *
+ * libdbus really should not call this, and mostly does not;
+ * recoverable errors should instead be reported to the caller, and
+ * bugs should be reported with _dbus_warn_check_failed(). Printing to
+ * stderr may be appropriate if something really should never happen
+ * but is not a bug in the application using libdbus, for example a
+ * misconfigured install of libdbus could be reported with
+ * _dbus_warn().
+ *
+ * @param format printf-style format string.
+ */
+void
+_dbus_warn (const char *format,
+ ...)
+{
+ va_list args;
+
+ if (!warn_initted)
+ init_warnings ();
+
+ va_start (args, format);
+ if (!_dbus_log_valist (DBUS_LOG_LEVEL_WARNING, format, args))
+ log_oom_warning (format);
+ va_end (args);
+
+ if (fatal_warnings)
+ {
+ _dbus_abort ();
+ }
+}
+
+/**
+ * Indicates that an assertion failed due to an application bug, as
+ * described in the given message. The "bug handler" from
+ * dbus_set_bug_handler() will be invoked and usually a log handler
+ * from dbus_set_log_handler() will be invoked as well.
+ *
+ * The message logged will contain the process ID of the failing app.
+ *
+ * This function 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;
+ DBusBugHandlingAction action;
+ DBusString str;
+
+ action = DBUS_BUG_ACTION_LEAVE_DEFAULT;
+ if (bug_handler.func != NULL)
+ {
+ action = (* bug_handler.func) (bug_handler.data);
+ if (action == DBUS_BUG_ACTION_FORCE_SILENTLY_IGNORE)
+ return;
+ }
+
+ if (!warn_initted)
+ init_warnings ();
+
+ va_start (args, format);
+ if (_dbus_string_init (&str) &&
+ _dbus_string_append_printf (&str, "process %lu: ", _dbus_getpid ()) &&
+ _dbus_string_append_printf_valist (&str, format, args))
+ {
+ _dbus_log_literal (DBUS_LOG_LEVEL_BUG, &str);
+ }
+ else
+ {
+ log_oom_warning (format);
+ }
+ va_end (args);
+
+ _dbus_string_free (&str);
+
+ if ((action == DBUS_BUG_ACTION_LEAVE_DEFAULT && fatal_bugs) ||
+ action == DBUS_BUG_ACTION_FORCE_LOG_AND_ABORT)
+ {
+ _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
+
+static inline void
+_dbus_verbose_init (void)
+{
+ if (!verbose_initted)
+ {
+ const char *p = _dbus_getenv ("DBUS_VERBOSE");
+ verbose = p != NULL && *p == '1';
+ verbose_initted = TRUE;
+ }
+}
+
+/**
+ * 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)
+{
+ _dbus_verbose_init ();
+ return verbose;
+}
+
+/**
+ * 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_real (const char *format,
+ ...)
+{
+ va_list args;
+ static dbus_bool_t need_pid = TRUE;
+ int len;
+ DBusString str;
+
+ /* things are written a bit oddly here so that
+ * in the non-verbose case we just have the one
+ * conditional and return immediately.
+ */
+ if (!_dbus_is_verbose_real())
+ return;
+
+ if (!_dbus_string_init (&str))
+ {
+ log_oom_warning (format);
+ return;
+ }
+
+ /* Print out pid before the line */
+ if (need_pid)
+ {
+#if PTHREAD_IN_VERBOSE
+ if (!_dbus_string_append_printf (&str, "%lu: 0x%lx: ", _dbus_getpid (), pthread_self ()))
+ {
+ log_oom_warning (format);
+ _dbus_string_free (&str);
+ return;
+ }
+#else
+ if (!_dbus_string_append_printf (&str, "%lu: ", _dbus_getpid ()))
+ {
+ log_oom_warning (format);
+ _dbus_string_free (&str);
+ return;
+ }
+#endif
+ }
+
+ /* Only print pid again if the next line is a new line */
+ len = strlen (format);
+ if (format[len-1] == '\n')
+ need_pid = TRUE;
+ else
+ need_pid = FALSE;
+
+ va_start (args, format);
+ if (!_dbus_string_append_printf_valist (&str, format, args))
+ {
+ log_oom_warning (format);
+ _dbus_string_free (&str);
+ return;
+ }
+ _dbus_log_literal (DBUS_LOG_LEVEL_VERBOSE, &str);
+ va_end (args);
+
+ _dbus_string_free (&str);
+}
+
+/**
+ * Reinitializes the verbose logging code, used
+ * as a hack in dbus-spawn.c so that a child
+ * process re-reads its pid
+ *
+ */
+void
+_dbus_verbose_reset_real (void)
+{
+ verbose_initted = FALSE;
+}
+
+#endif /* DBUS_ENABLE_VERBOSE_MODE */
+
+static dbus_bool_t handlers_initialized = FALSE;
+
+static void
+handlers_shutdown_func (void *data)
+{
+ dbus_set_log_handler (DBUS_LOG_LEVEL_BUG, NULL, NULL, NULL);
+ dbus_set_log_handler (DBUS_LOG_LEVEL_WARNING, NULL, NULL, NULL);
+ dbus_set_log_handler (DBUS_LOG_LEVEL_VERBOSE, NULL, NULL, NULL);
+
+ dbus_set_bug_handler (NULL, NULL, NULL);
+
+ handlers_initialized = FALSE;
+}
+
+static void
+init_handlers(void)
+{
+ if (!handlers_initialized)
+ {
+ if (!_dbus_register_shutdown_func (handlers_shutdown_func,
+ NULL))
+ {
+ /* For now, we silently eat this OOM error. I think the
+ * right fix is to move the "ShutdownClosure" in the
+ * _dbus_register_shutdown_func implementation to a public
+ * header, and have each place that registers a shutdown
+ * func allocate it as a static global var. But it's
+ * not like there should be any problem here in any real
+ * app.
+ */
+
+ return;
+ }
+
+ handlers_initialized = TRUE;
+ }
+}
+
+/** @} */ /* End of internals */
+
/**
* @defgroup DBusMisc Miscellaneous
@@ -35,10 +451,214 @@
*/
/**
+ * Sets a handler function for printing messages.
+ *
+ * libdbus can print several kinds of message, each kind has a log level.
+ *
+ * Each log level may have a handler replacing the default dbus
+ * handler for that log level. The default handler prints the message
+ * to the console (stdout or stderr).
+ *
+ * See the documentation on each level in the #DBusLogLevel enum
+ * documentation.
+ *
+ * A typical use of this function is to use syslog() instead of
+ * printing to the console.
+ *
+ * @note When implementing this function, remember that the message passed
+ * to your log handler is <em>NOT</em> a format string. So for
+ * example, you want to write syslog("%s", message) rather than
+ * syslog(message).
+ *
+ * @note It's a bad idea to make any D-Bus calls from inside the log
+ * handler, because D-Bus will not have dropped any thread locks it has,
+ * and if the log handler is called for an assertion failure and
+ * the additional D-Bus calls fail again, you could get into an
+ * infinite recursion.
+ *
+ * @note The log handler can be called from any thread that libdbus
+ * is called from.
+ *
+ * @note dbus_set_log_handler() does not have any thread locking, so
+ * should only be called at application initialization time before
+ * multiple threads are in use with libdbus.
+ *
+ * @since 1.2
+ *
+ * @param level log level to set the function for
+ * @param func function to be invoked to handle log messages, or #NULL to restore the default
+ * @param func_data data to pass to the function
+ * @param free_data_func function used to free the data if a new log handler is set at the same level or dbus_shutdown() is invoked
+ *
+ */
+void
+dbus_set_log_handler (DBusLogLevel level,
+ DBusLogHandlerFunction func,
+ void *func_data,
+ DBusFreeFunction free_data_func)
+{
+ DBusLogHandler *handler;
+ DBusFreeFunction old_func;
+ void *old_data;
+
+ _dbus_return_if_fail (level == DBUS_LOG_LEVEL_BUG ||
+ level == DBUS_LOG_LEVEL_VERBOSE ||
+ level == DBUS_LOG_LEVEL_WARNING);
+
+ init_handlers ();
+
+ handler = get_handler (level);
+
+ _dbus_assert (handler != NULL);
+
+ if (handler->free_data_func)
+ {
+ old_func = handler->free_data_func;
+ old_data = handler->data;
+ }
+ else
+ {
+ old_func = NULL;
+ old_data = NULL;
+ }
+
+ handler->func = func;
+ handler->data = func_data;
+ handler->free_data_func = free_data_func;
+
+ if (old_func != NULL)
+ {
+ (* old_func) (old_data);
+ }
+}
+
+/**
+ * Sets a function to be called when libdbus detects an application
+ * bug.
+ *
+ * The bug handler function is called when libdbus detects that the
+ * application programmer is using libdbus in an undefined way. The
+ * most common bug detected by libdbus is that a passed-in pointer is
+ * #NULL when it is not allowed to be #NULL according to the
+ * documented ABI contract. However, there are a number of others bugs
+ * libdbus checks for.
+ *
+ * The bug handler can do anything you like, and then returns a value
+ * telling libdbus what further action to take. See the documentation
+ * for each value in the #DBusBugHandlingAction enum for more.
+ *
+ * Most applications should not set a bug handler, or at least should
+ * return #DBUS_BUG_ACTION_LEAVE_DEFAULT to use the default bug
+ * handling behavior. For example, distributions may set
+ * DBUS_FATAL_WARNINGS=1 to make bugs fatal during a beta test cycle
+ * and then set DBUS_FATAL_WARNINGS=0 to make bugs nonfatal in
+ * production releases. Applications that don't honor this setting may
+ * be considered antisocial unless they are special in some way.
+ *
+ * There are few semantic guarantees surrounding bug handling. libdbus
+ * often will not be able to recover gracefully after a bug is
+ * detected; you should think of a detected bug much as you would
+ * think of a segmentation fault or illegal memory access.
+ *
+ * If you continue after a bug is reported, most likely some expected
+ * function was not performed; this may cause a cascading effect of
+ * additional bugs or crashes, in libdbus or in your application.
+ *
+ * libdbus behavior after reporting a bug is completely undefined.
+ * Remember that memory may be corrupted, data structures may be
+ * inconsistent, or a function may have behaved unexpectedly. If you
+ * ask libdbus to ignore a bug, your program may be able to continue,
+ * or it may not.
+ *
+ * Ignoring bugs is strongly discouraged in security-sensitive
+ * applications or daemons; you do not want a security-sensitive
+ * program to continue in a confused or unknown state.
+ * #DBUS_BUG_ACTION_FORCE_LOG_AND_ABORT is advisable in secure
+ * daemons. Of course, it's also advisable to avoid having bugs in
+ * these daemons in the first place, so be careful.
+ *
+ * There are no guarantees that bugs will be detected; libdbus can be
+ * compiled with "checks" disabled, which will mean it does not check
+ * for application bugs. Also, no specific bug checks are guaranteed
+ * to exist, since there may be performance or other reasons to omit
+ * certain checks. The checks that exist may change over time. They
+ * are not considered part of the ABI contract.
+ *
+ * The behavior of #DBUS_BUG_ACTION_LEAVE_DEFAULT is not defined by the
+ * ABI contract. Right now it varies based on the DBUS_FATAL_WARNINGS
+ * environment variable but this is not guaranteed.
+ *
+ * If you ask libdbus to log the bug, it may call the log handler
+ * at level #DBUS_LOG_LEVEL_BUG multiple times to log a multi-line
+ * problem. So if you want to do something once per bug, do it
+ * in the bug handler, not the log handler.
+ *
+ * @note <em>libdbus bug checks will never be triggered for error conditions
+ * outside of the application author's control.</em> Recoverable
+ * errors are reported to the caller of the function as documented for
+ * that function. See #DBusError for some more details on recoverable
+ * error conditions.
+ *
+ * @note Again: any time the bug handler function is invoked, it is a bug
+ * that you can fix, and the bug most likely has serious or
+ * potentially serious consequences. This is a <em>bug</em> handler,
+ * not an error handler. In a correct application, the bug handler
+ * will never be used. That's why it's called the bug handler.
+ *
+ * @note It's a bad idea to make any D-Bus calls from inside the bug
+ * handler, because D-Bus will not have dropped any thread locks it has
+ * and in general will be in who-knows-what state.
+ *
+ * @note The bug handler can be called from any thread that libdbus
+ * is called from.
+ *
+ * @note dbus_set_bug_handler() does not have any thread locking, so
+ * should only be called at application initialization time before
+ * multiple threads are in use with libdbus.
+ *
+ * @since 1.2
+ *
+ * @param func function to be invoked to handle bugs, or #NULL to restore the default
+ * @param func_data data to pass to the function
+ * @param free_data_func function used to free the data if a new bug handler is set or dbus_shutdown() is invoked
+ *
+ */
+void
+dbus_set_bug_handler (DBusBugHandlerFunction func,
+ void *func_data,
+ DBusFreeFunction free_data_func)
+{
+ DBusFreeFunction old_func;
+ void *old_data;
+
+ init_handlers ();
+
+ if (bug_handler.free_data_func)
+ {
+ old_func = bug_handler.free_data_func;
+ old_data = bug_handler.data;
+ }
+ else
+ {
+ old_func = NULL;
+ old_data = NULL;
+ }
+
+ bug_handler.func = func;
+ bug_handler.data = func_data;
+ bug_handler.free_data_func = free_data_func;
+
+ if (old_func != NULL)
+ {
+ (* old_func) (old_data);
+ }
+}
+
+/**
* Obtains the machine UUID of the machine this process is running on.
*
* The returned string must be freed with dbus_free().
- *
+ *
* This UUID is guaranteed to remain the same until the next reboot
* (unless the sysadmin foolishly changes it and screws themselves).
* It will usually remain the same across reboots also, but hardware
@@ -99,11 +719,157 @@ dbus_get_local_machine_id (void)
#include "dbus-test.h"
#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+/* _dbus_strdup would get involved in the OOM handling testing,
+ * we want to keep this outside of dbus_malloc
+ */
+static char*
+xstrdup (const char *str)
+{
+ size_t len;
+ char *copy;
+
+ if (str == NULL)
+ return NULL;
+
+ len = strlen (str);
+ copy = malloc (len + 1);
+ if (copy == NULL)
+ return NULL;
+
+ memcpy (copy, str, len + 1);
+
+ return copy;
+}
+
+static DBusBugHandlingAction
+bug_handler_log_and_ignore(void *data)
+{
+ return DBUS_BUG_ACTION_FORCE_LOG_AND_IGNORE;
+}
+
+static void
+log_handler_that_copies_message (const char *message,
+ void *data)
+{
+ char** m_p = data;
+ if (*m_p == NULL) /* multiple calls are allowed */
+ *m_p = xstrdup (message);
+}
+
+static void
+log_handler_free_message (void *data)
+{
+ char** m_p = data;
+ free (*m_p);
+ *m_p = NULL;
+}
+
+static void
+check_resulting_message (const char *m,
+ const char *expected)
+{
+ if (m == NULL)
+ {
+ fprintf (stderr, "log message should have been saved, expecting '%s'\n", expected);
+ exit (1);
+ }
+
+ /* strstr not strcmp because the log stuff can add the pid */
+ if (strstr(m, expected) == NULL)
+ {
+ fprintf (stderr, "wrong log message '%s' was expecting '%s'\n", m, expected);
+ exit (1);
+ }
+}
+
+static dbus_bool_t
+test_log_handler(void *data)
+{
+ char *m;
+ DBusLogLevel level;
+
+ level = _DBUS_POINTER_TO_INT (data);
+
+ m = NULL;
+ dbus_set_log_handler (level, log_handler_that_copies_message,
+ &m,
+ log_handler_free_message);
+ switch (level)
+ {
+ case DBUS_LOG_LEVEL_BUG:
+#ifndef DBUS_DISABLE_CHECKS
+ dbus_set_bug_handler (bug_handler_log_and_ignore, NULL, NULL);
+ _dbus_warn_check_failed ("Testing a check failed");
+ dbus_set_bug_handler (NULL, NULL, NULL);
+
+ check_resulting_message (m, "Testing a check failed");
+#endif
+ break;
+ case DBUS_LOG_LEVEL_WARNING:
+ {
+ dbus_bool_t old;
+
+ init_warnings ();
+
+ old = fatal_warnings;
+ fatal_warnings = FALSE;
+ _dbus_warn ("Testing a warning");
+ fatal_warnings = old;
+ check_resulting_message (m, "Testing a warning");
+ }
+ break;
+ case DBUS_LOG_LEVEL_VERBOSE:
+#ifdef DBUS_ENABLE_VERBOSE_MODE
+ {
+ dbus_bool_t old;
+
+ _dbus_verbose_init ();
+
+ old = verbose;
+ verbose = TRUE;
+ _dbus_verbose ("Testing verbose logging");
+ verbose = old;
+ check_resulting_message (m, "Testing verbose logging");
+ }
+#endif /* Verbose mode */
+ break;
+ }
+
+ dbus_set_log_handler (level, NULL, NULL, NULL);
+
+ if (m != NULL)
+ {
+ fprintf (stderr, "Free function apparently not called for log at level %d\n", level);
+ exit (1);
+ }
+
+ return TRUE;
+}
dbus_bool_t
_dbus_misc_test (void)
{
+ /* The OOM tests would not work right now, since we just don't log
+ * when we run out, and print an annoying warning to boot. There's
+ * a small issue with setting log handlers when out of memory, also.
+ */
+
+#ifndef DBUS_DISABLE_CHECKS
+ test_log_handler (_DBUS_INT_TO_POINTER (DBUS_LOG_LEVEL_BUG));
+ /* _dbus_test_oom_handling("bug logging", test_log_handler, _DBUS_INT_TO_POINTER (DBUS_LOG_LEVEL_BUG)); */
+#endif
+
+#ifdef DBUS_ENABLE_VERBOSE_MODE
+ test_log_handler (_DBUS_INT_TO_POINTER (DBUS_LOG_LEVEL_VERBOSE));
+ /* _dbus_test_oom_handling("verbose logging", test_log_handler, _DBUS_INT_TO_POINTER (DBUS_LOG_LEVEL_VERBOSE)); */
+#endif
+
+ test_log_handler (_DBUS_INT_TO_POINTER (DBUS_LOG_LEVEL_WARNING));
+ /* _dbus_test_oom_handling("warn logging", test_log_handler, _DBUS_INT_TO_POINTER (DBUS_LOG_LEVEL_WARNING)); */
return TRUE;
}
Index: dbus/dbus-misc.h
===================================================================
RCS file: /cvs/dbus/dbus/dbus/dbus-misc.h,v
retrieving revision 1.2
diff -u -p -r1.2 dbus-misc.h
--- dbus/dbus-misc.h 15 Nov 2006 03:07:59 -0000 1.2
+++ dbus/dbus-misc.h 25 Nov 2006 01:59:25 -0000
@@ -29,6 +29,7 @@
#include <dbus/dbus-types.h>
#include <dbus/dbus-errors.h>
+#include <dbus/dbus-memory.h>
DBUS_BEGIN_DECLS
@@ -37,8 +38,113 @@ DBUS_BEGIN_DECLS
* @{
*/
-char* dbus_get_local_machine_id (void);
+/** Distinguishes kinds of log message. */
+typedef enum {
+ DBUS_LOG_LEVEL_BUG, /**< Used to log bugs. Handler called only if the
+ * #DBusBugHandlerFunction indicates that
+ * the bug should be logged. Only called
+ * if libdbus was compiled with "checks" or
+ * "asserts" enabled.
+ * @since 1.2
+ */
+ DBUS_LOG_LEVEL_WARNING, /**< Used to log warnings. There are very few of these,
+ * they occur when a something goes horribly
+ * wrong and it's not a defined recoverable
+ * error returned by the API and it's not a
+ * programming mistake on anyone's part.
+ * Might be used for example if the libdbus
+ * installation is broken.
+ * @since 1.2
+ */
+ DBUS_LOG_LEVEL_VERBOSE /**< Used to log "debug spew" only if libdbus
+ * was compiled with verbose mode available and
+ * verbose mode is currently enabled via
+ * environment variable or other configuration.
+ * @since 1.2
+ */
+} DBusLogLevel;
+
+/** Returned by the #DBusBugHandlerFunction to indicate what libdbus should do next. */
+typedef enum {
+ DBUS_BUG_ACTION_LEAVE_DEFAULT, /**< Most bug handlers should
+ * return this value, which
+ * indicates that the default
+ * action should be taken;
+ * the default action may be
+ * controlled by environment
+ * variables, config files,
+ * or compile-time options.
+ * The default action is not
+ * guaranteed by the ABI
+ * contract, but right now is
+ * to log the bug and then
+ * optionally exit depending
+ * on the DBUS_FATAL_WARNINGS
+ * environment variable.
+ *
+ * @since 1.2
+ */
+ DBUS_BUG_ACTION_FORCE_LOG_AND_ABORT, /**< Force libdbus to call
+ * the log handler at level
+ * #DBUS_LOG_LEVEL_BUG and
+ * then exit the program,
+ * regardless of environment
+ * variables or
+ * configuration.
+ *
+ * @since 1.2
+ */
+ DBUS_BUG_ACTION_FORCE_LOG_AND_IGNORE, /**< Force libdbus to call
+ * the log handler at level
+ * #DBUS_LOG_LEVEL_BUG and
+ * then do nothing further,
+ * regardless of environment
+ * variables or
+ * configuration.
+ *
+ * @note The behavior of
+ * libdbus is undefined after
+ * the bug handler returns;
+ * it may well crash,
+ * immediately or at a later
+ * time.
+ *
+ * @since 1.2
+ */
+ DBUS_BUG_ACTION_FORCE_SILENTLY_IGNORE /**< Force libdbus to do
+ * nothing, regardless of
+ * environment variables or
+ * configuration.
+ *
+ * @note The behavior of
+ * libdbus is undefined after
+ * the bug handler returns;
+ * it may well crash,
+ * immediately or at a later
+ * time.
+ *
+ * @since 1.2
+ */
+} DBusBugHandlingAction;
+
+
+/** Bug handler function, see dbus_set_bug_handler(). @since 1.2 */
+typedef DBusBugHandlingAction (* DBusBugHandlerFunction) (void *data);
+
+/** Log handler function, see dbus_set_log_handler(). @since 1.2 */
+typedef void (* DBusLogHandlerFunction) (const char *message,
+ void *data);
+
+void dbus_set_log_handler (DBusLogLevel level,
+ DBusLogHandlerFunction func,
+ void *func_data,
+ DBusFreeFunction free_data_func);
+void dbus_set_bug_handler (DBusBugHandlerFunction func,
+ void *func_data,
+ DBusFreeFunction free_data_func);
+char* dbus_get_local_machine_id (void);
+
/** @} */
DBUS_END_DECLS
Index: dbus/dbus-sysdeps.c
===================================================================
RCS file: /cvs/dbus/dbus/dbus/dbus-sysdeps.c,v
retrieving revision 1.118
diff -u -p -r1.118 dbus-sysdeps.c
--- dbus/dbus-sysdeps.c 21 Oct 2006 18:17:02 -0000 1.118
+++ dbus/dbus-sysdeps.c 25 Nov 2006 01:59:26 -0000
@@ -70,8 +70,14 @@ void
_dbus_abort (void)
{
const char *s;
+
+ fflush (stderr); /* in case stuff was logged prior to abort. I guess stderr
+ * is often not buffered anyway.
+ */
_dbus_print_backtrace ();
+
+ fflush (stderr);
s = _dbus_getenv ("DBUS_BLOCK_ON_ABORT");
if (s && *s)
More information about the dbus
mailing list