summaryrefslogtreecommitdiff
path: root/util/messages.c
diff options
context:
space:
mode:
authorRuss Allbery <rra@stanford.edu>2007-10-04 22:21:19 +0000
committerRuss Allbery <rra@stanford.edu>2007-10-04 22:21:19 +0000
commit9ff667addf39128f43d08d4ec56a6a94ec3bb062 (patch)
tree41cd39045fb2d37d343608af57aebf844ecd5690 /util/messages.c
parent2f9387bdf0e047bbd193532c4fed209acabd0e7a (diff)
Initial import of a C portability framework and utility functions from
remctl so that the wallet client error handling can rest on a firmer foundation.
Diffstat (limited to 'util/messages.c')
-rw-r--r--util/messages.c361
1 files changed, 361 insertions, 0 deletions
diff --git a/util/messages.c b/util/messages.c
new file mode 100644
index 0000000..4a975c3
--- /dev/null
+++ b/util/messages.c
@@ -0,0 +1,361 @@
+/* $Id$
+**
+** Message and error reporting (possibly fatal).
+**
+** Usage:
+**
+** extern int cleanup(void);
+** extern void log(int, const char *, va_list, int);
+**
+** message_fatal_cleanup = cleanup;
+** message_program_name = argv[0];
+**
+** warn("Something horrible happened at %lu", time);
+** syswarn("Couldn't unlink temporary file %s", tmpfile);
+**
+** die("Something fatal happened at %lu", time);
+** sysdie("open of %s failed", filename);
+**
+** debug("Some debugging message about %s", string);
+** notice("Informational notices");
+**
+** message_handlers_warn(1, log);
+** warn("This now goes through our log function");
+**
+** These functions implement message reporting through user-configurable
+** handler functions. debug() only does something if DEBUG is defined, and
+** notice() and warn() just output messages as configured. die() similarly
+** outputs a message but then exits, normally with a status of 1.
+**
+** The sys* versions do the same, but append a colon, a space, and the
+** results of strerror(errno) to the end of the message. All functions
+** accept printf-style formatting strings and arguments.
+**
+** If message_fatal_cleanup is non-NULL, it is called before exit by die and
+** sysdie and its return value is used as the argument to exit. It is a
+** pointer to a function taking no arguments and returning an int, and can be
+** used to call cleanup functions or to exit in some alternate fashion (such
+** as by calling _exit).
+**
+** If message_program_name is non-NULL, the string it points to, followed by
+** a colon and a space, is prepended to all error messages logged through the
+** message_log_stdout and message_log_stderr message handlers (the former is
+** the default for notice, and the latter is the default for warn and die).
+**
+** Honoring error_program_name and printing to stderr is just the default
+** handler; with message_handlers_* the handlers for any message function can
+** be changed. By default, notice prints to stdout, warn and die print to
+** stderr, and the others don't do anything at all. These functions take a
+** count of handlers and then that many function pointers, each one to a
+** function that takes a message length (the number of characters snprintf
+** generates given the format and arguments), a format, an argument list as a
+** va_list, and the applicable errno value (if any).
+**
+** Copyright (c) 2004, 2005, 2006
+** by Internet Systems Consortium, Inc. ("ISC")
+** Copyright (c) 1991, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001,
+** 2002, 2003 by The Internet Software Consortium and Rich Salz
+**
+** This code is derived from software contributed to the Internet Software
+** Consortium by Rich Salz.
+**
+** Permission to use, copy, modify, and distribute this software for any
+** purpose with or without fee is hereby granted, provided that the above
+** copyright notice and this permission notice appear in all copies.
+**
+** THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+** REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+** MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY
+** SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+** WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+** ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+** OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+#include <config.h>
+#include <system.h>
+
+#include <errno.h>
+#include <syslog.h>
+
+#include <util/util.h>
+
+/* The default handler lists. */
+static message_handler_func stdout_handlers[2] = {
+ message_log_stdout, NULL
+};
+static message_handler_func stderr_handlers[2] = {
+ message_log_stderr, NULL
+};
+
+/* The list of logging functions currently in effect. */
+static message_handler_func *debug_handlers = NULL;
+static message_handler_func *notice_handlers = stdout_handlers;
+static message_handler_func *warn_handlers = stderr_handlers;
+static message_handler_func *die_handlers = stderr_handlers;
+
+/* If non-NULL, called before exit and its return value passed to exit. */
+int (*message_fatal_cleanup)(void) = NULL;
+
+/* If non-NULL, prepended (followed by ": ") to messages. */
+const char *message_program_name = NULL;
+
+
+/*
+** Set the handlers for a particular message function. Takes a pointer to
+** the handler list, the count of handlers, and the argument list.
+*/
+static void
+message_handlers(message_handler_func **list, int count, va_list args)
+{
+ int i;
+
+ if (*list != stdout_handlers && *list != stderr_handlers)
+ free(*list);
+ *list = xmalloc(sizeof(message_handler_func) * (count + 1));
+ for (i = 0; i < count; i++)
+ (*list)[i] = (message_handler_func) va_arg(args, message_handler_func);
+ (*list)[count] = NULL;
+}
+
+
+/*
+** There's no good way of writing these handlers without a bunch of code
+** duplication since we can't assume variadic macros, but I can at least make
+** it easier to write and keep them consistent.
+*/
+#define HANDLER_FUNCTION(type) \
+ void \
+ message_handlers_ ## type(int count, ...) \
+ { \
+ va_list args; \
+ \
+ va_start(args, count); \
+ message_handlers(& type ## _handlers, count, args); \
+ va_end(args); \
+ }
+HANDLER_FUNCTION(debug)
+HANDLER_FUNCTION(notice)
+HANDLER_FUNCTION(warn)
+HANDLER_FUNCTION(die)
+
+
+/*
+** Print a message to stdout, supporting message_program_name.
+*/
+void
+message_log_stdout(int len UNUSED, const char *fmt, va_list args, int err)
+{
+ if (message_program_name != NULL)
+ fprintf(stdout, "%s: ", message_program_name);
+ vfprintf(stdout, fmt, args);
+ if (err)
+ fprintf(stdout, ": %s", strerror(err));
+ fprintf(stdout, "\n");
+}
+
+
+/*
+** Print a message to stderr, supporting message_program_name. Also flush
+** stdout so that errors and regular output occur in the right order.
+*/
+void
+message_log_stderr(int len UNUSED, const char *fmt, va_list args, int err)
+{
+ fflush(stdout);
+ if (message_program_name != NULL)
+ fprintf(stderr, "%s: ", message_program_name);
+ vfprintf(stderr, fmt, args);
+ if (err)
+ fprintf(stderr, ": %s", strerror(err));
+ fprintf(stderr, "\n");
+}
+
+
+/*
+** Log a message to syslog. This is a helper function used to implement all
+** of the syslog message log handlers. It takes the same arguments as a
+** regular message handler function but with an additional priority
+** argument.
+*/
+static void
+message_log_syslog(int pri, int len, const char *fmt, va_list args, int err)
+{
+ char *buffer;
+
+ buffer = malloc(len + 1);
+ if (buffer == NULL) {
+ fprintf(stderr, "failed to malloc %u bytes at %s line %d: %s",
+ len + 1, __FILE__, __LINE__, strerror(errno));
+ exit(message_fatal_cleanup ? (*message_fatal_cleanup)() : 1);
+ }
+ vsnprintf(buffer, len + 1, fmt, args);
+ if (err == 0)
+ syslog(pri, "%s", buffer);
+ else
+ syslog(pri, "%s: %s", buffer, strerror(err));
+ free(buffer);
+}
+
+
+/*
+** Do the same sort of wrapper to generate all of the separate syslog logging
+** functions.
+*/
+#define SYSLOG_FUNCTION(name, type) \
+ void \
+ message_log_syslog_ ## name(int l, const char *f, va_list a, int e) \
+ { \
+ message_log_syslog(LOG_ ## type, l, f, a, e); \
+ }
+SYSLOG_FUNCTION(debug, DEBUG)
+SYSLOG_FUNCTION(info, INFO)
+SYSLOG_FUNCTION(notice, NOTICE)
+SYSLOG_FUNCTION(warning, WARNING)
+SYSLOG_FUNCTION(err, ERR)
+SYSLOG_FUNCTION(crit, CRIT)
+
+
+/*
+** All of the message handlers. There's a lot of code duplication here too,
+** but each one is still *slightly* different and va_start has to be called
+** multiple times, so it's hard to get rid of the duplication.
+*/
+
+void
+debug(const char *format, ...)
+{
+ va_list args;
+ message_handler_func *log;
+ int length;
+
+ if (debug_handlers == NULL)
+ return;
+ va_start(args, format);
+ length = vsnprintf(NULL, 0, format, args);
+ va_end(args);
+ if (length < 0)
+ return;
+ for (log = debug_handlers; *log != NULL; log++) {
+ va_start(args, format);
+ (**log)(length, format, args, 0);
+ va_end(args);
+ }
+}
+
+void
+notice(const char *format, ...)
+{
+ va_list args;
+ message_handler_func *log;
+ int length;
+
+ va_start(args, format);
+ length = vsnprintf(NULL, 0, format, args);
+ va_end(args);
+ if (length < 0)
+ return;
+ for (log = notice_handlers; *log != NULL; log++) {
+ va_start(args, format);
+ (**log)(length, format, args, 0);
+ va_end(args);
+ }
+}
+
+void
+sysnotice(const char *format, ...)
+{
+ va_list args;
+ message_handler_func *log;
+ int length;
+ int error = errno;
+
+ va_start(args, format);
+ length = vsnprintf(NULL, 0, format, args);
+ va_end(args);
+ if (length < 0)
+ return;
+ for (log = notice_handlers; *log != NULL; log++) {
+ va_start(args, format);
+ (**log)(length, format, args, error);
+ va_end(args);
+ }
+}
+
+void
+warn(const char *format, ...)
+{
+ va_list args;
+ message_handler_func *log;
+ int length;
+
+ va_start(args, format);
+ length = vsnprintf(NULL, 0, format, args);
+ va_end(args);
+ if (length < 0)
+ return;
+ for (log = warn_handlers; *log != NULL; log++) {
+ va_start(args, format);
+ (**log)(length, format, args, 0);
+ va_end(args);
+ }
+}
+
+void
+syswarn(const char *format, ...)
+{
+ va_list args;
+ message_handler_func *log;
+ int length;
+ int error = errno;
+
+ va_start(args, format);
+ length = vsnprintf(NULL, 0, format, args);
+ va_end(args);
+ if (length < 0)
+ return;
+ for (log = warn_handlers; *log != NULL; log++) {
+ va_start(args, format);
+ (**log)(length, format, args, error);
+ va_end(args);
+ }
+}
+
+void
+die(const char *format, ...)
+{
+ va_list args;
+ message_handler_func *log;
+ int length;
+
+ va_start(args, format);
+ length = vsnprintf(NULL, 0, format, args);
+ va_end(args);
+ if (length >= 0)
+ for (log = die_handlers; *log != NULL; log++) {
+ va_start(args, format);
+ (**log)(length, format, args, 0);
+ va_end(args);
+ }
+ exit(message_fatal_cleanup ? (*message_fatal_cleanup)() : 1);
+}
+
+void
+sysdie(const char *format, ...)
+{
+ va_list args;
+ message_handler_func *log;
+ int length;
+ int error = errno;
+
+ va_start(args, format);
+ length = vsnprintf(NULL, 0, format, args);
+ va_end(args);
+ if (length >= 0)
+ for (log = die_handlers; *log != NULL; log++) {
+ va_start(args, format);
+ (**log)(length, format, args, error);
+ va_end(args);
+ }
+ exit(message_fatal_cleanup ? (*message_fatal_cleanup)() : 1);
+}