|
|
|
/* $Id: messages.c 5496 2002-06-07 13:59:06Z alexk $
|
|
|
|
**
|
|
|
|
** 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);
|
|
|
|
** trace(TRACE_PROGRAM, "Program trace output");
|
|
|
|
** 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,
|
|
|
|
** trace() supports sending trace messages in one of a number of configurable
|
|
|
|
** classes of traces so that they can be turned on or off independently, 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).
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* Used for unused parameters to silence gcc warnings. */
|
|
|
|
#define UNUSED __attribute__((__unused__))
|
|
|
|
|
|
|
|
/* Make available the bool type. */
|
|
|
|
#if INN_HAVE_STDBOOL_H
|
|
|
|
# include <stdbool.h>
|
|
|
|
#else
|
|
|
|
# undef true
|
|
|
|
# undef false
|
|
|
|
# define true (1)
|
|
|
|
# define false (0)
|
|
|
|
# ifndef __cplusplus
|
|
|
|
# define bool int
|
|
|
|
# endif
|
|
|
|
#endif /* INN_HAVE_STDBOOL_H */
|
|
|
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <stdarg.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <syslog.h>
|
|
|
|
#include <crypt.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <pwd.h>
|
|
|
|
#include <grp.h>
|
|
|
|
|
|
|
|
#include "xmalloc.h"
|
|
|
|
|
|
|
|
/* These are the currently-supported types of traces. */
|
|
|
|
enum message_trace {
|
|
|
|
TRACE_NETWORK, /* Network traffic. */
|
|
|
|
TRACE_PROGRAM, /* Stages of program execution. */
|
|
|
|
TRACE_ALL /* All traces; this must be last. */
|
|
|
|
};
|
|
|
|
|
|
|
|
/* The reporting functions. The ones prefaced by "sys" add a colon, a space,
|
|
|
|
and the results of strerror(errno) to the output and are intended for
|
|
|
|
reporting failures of system calls. */
|
|
|
|
extern void trace(enum message_trace, const char *, ...)
|
|
|
|
__attribute__((__format__(printf, 2, 3)));
|
|
|
|
extern void notice(const char *, ...)
|
|
|
|
__attribute__((__format__(printf, 1, 2)));
|
|
|
|
extern void sysnotice(const char *, ...)
|
|
|
|
__attribute__((__format__(printf, 1, 2)));
|
|
|
|
extern void warn(const char *, ...)
|
|
|
|
__attribute__((__format__(printf, 1, 2)));
|
|
|
|
extern void syswarn(const char *, ...)
|
|
|
|
__attribute__((__format__(printf, 1, 2)));
|
|
|
|
extern void die(const char *, ...)
|
|
|
|
__attribute__((__noreturn__, __format__(printf, 1, 2)));
|
|
|
|
extern void sysdie(const char *, ...)
|
|
|
|
__attribute__((__noreturn__, __format__(printf, 1, 2)));
|
|
|
|
|
|
|
|
/* Debug is handled specially, since we want to make the code disappear
|
|
|
|
completely unless we're built with -DDEBUG. We can only do that with
|
|
|
|
support for variadic macros, though; otherwise, the function just won't do
|
|
|
|
anything. */
|
|
|
|
#if !defined(DEBUG) && (INN_HAVE_C99_VAMACROS || INN_HAVE_GNU_VAMACROS)
|
|
|
|
# if INN_HAVE_C99_VAMACROS
|
|
|
|
# define debug(format, ...) /* empty */
|
|
|
|
# elif INN_HAVE_GNU_VAMACROS
|
|
|
|
# define debug(format, args...) /* empty */
|
|
|
|
# endif
|
|
|
|
#else
|
|
|
|
extern void debug(const char *, ...)
|
|
|
|
__attribute__((__format__(printf, 1, 2)));
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* Set the handlers for various message functions. All of these functions
|
|
|
|
take a count of the number of handlers and then function pointers for each
|
|
|
|
of those handlers. These functions are not thread-safe; they set global
|
|
|
|
variables. */
|
|
|
|
extern void message_handlers_debug(int count, ...);
|
|
|
|
extern void message_handlers_trace(int count, ...);
|
|
|
|
extern void message_handlers_notice(int count, ...);
|
|
|
|
extern void message_handlers_warn(int count, ...);
|
|
|
|
extern void message_handlers_die(int count, ...);
|
|
|
|
|
|
|
|
/* Enable or disable tracing for particular classes of messages. */
|
|
|
|
extern void message_trace_enable(enum message_trace, bool);
|
|
|
|
|
|
|
|
/* Some useful handlers, intended to be passed to message_handlers_*. All
|
|
|
|
handlers take the length of the formatted message, the format, a variadic
|
|
|
|
argument list, and the errno setting if any. */
|
|
|
|
extern void message_log_stdout(int, const char *, va_list, int);
|
|
|
|
extern void message_log_stderr(int, const char *, va_list, int);
|
|
|
|
extern void message_log_syslog_debug(int, const char *, va_list, int);
|
|
|
|
extern void message_log_syslog_info(int, const char *, va_list, int);
|
|
|
|
extern void message_log_syslog_notice(int, const char *, va_list, int);
|
|
|
|
extern void message_log_syslog_warning(int, const char *, va_list, int);
|
|
|
|
extern void message_log_syslog_err(int, const char *, va_list, int);
|
|
|
|
extern void message_log_syslog_crit(int, const char *, va_list, int);
|
|
|
|
|
|
|
|
/* The type of a message handler. */
|
|
|
|
typedef void (*message_handler_func)(int, const char *, va_list, int);
|
|
|
|
|
|
|
|
/* If non-NULL, called before exit and its return value passed to exit. */
|
|
|
|
extern int (*message_fatal_cleanup)(void);
|
|
|
|
|
|
|
|
/* If non-NULL, prepended (followed by ": ") to all messages printed by either
|
|
|
|
message_log_stdout or message_log_stderr. */
|
|
|
|
extern const char *message_program_name;
|
|
|
|
|
|
|
|
/* 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 *trace_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;
|
|
|
|
|
|
|
|
/* Whether or not we're currently outputting a particular type of trace. */
|
|
|
|
static bool tracing[TRACE_ALL] = { false /* false, ... */ };
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
** 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(trace)
|
|
|
|
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);
|
|
|
|
syslog(pri, err ? "%s: %m" : "%s", buffer);
|
|
|
|
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)
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
** Enable or disable tracing for particular classes of messages.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
message_trace_enable(enum message_trace type, bool enable)
|
|
|
|
{
|
|
|
|
if (type > TRACE_ALL)
|
|
|
|
return;
|
|
|
|
if (type == TRACE_ALL) {
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < TRACE_ALL; i++)
|
|
|
|
tracing[i] = enable;
|
|
|
|
} else {
|
|
|
|
tracing[type] = enable;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
** 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.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#elif !INN_HAVE_C99_VAMACROS && !INN_HAVE_GNU_VAMACROS
|
|
|
|
void debug(const char *format UNUSED, ...) { }
|
|
|
|
#endif
|
|
|
|
|
|
|
|
void
|
|
|
|
trace(enum message_trace type, const char *format, ...)
|
|
|
|
{
|
|
|
|
va_list args;
|
|
|
|
message_handler_func *log;
|
|
|
|
int length;
|
|
|
|
|
|
|
|
if (trace_handlers == NULL || !tracing[type])
|
|
|
|
return;
|
|
|
|
va_start(args, format);
|
|
|
|
length = vsnprintf(NULL, 0, format, args);
|
|
|
|
va_end(args);
|
|
|
|
if (length < 0)
|
|
|
|
return;
|
|
|
|
for (log = trace_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);
|
|
|
|
}
|