/* -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- */
/* Copyright (c) 2001-2007 International Computer Science Institute
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software")
* to deal in the Software without restriction, subject to the conditions
* listed in the XORP LICENSE file. These conditions include: you must
* preserve this copyright notice, and you cannot mention the copyright
* holders in advertising related to the Software without their permission.
* The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
* notice is a summary of the XORP LICENSE file; the license in that file is
* legally binding.
*/
#ident "$XORP: xorp/libxorp/xlog.c,v 1.22 2007/02/16 22:46:29 pavlin Exp $"
/*
* Message logging utility.
*/
#include "libxorp_module.h"
#include "libxorp/xorp.h"
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif
#include "xlog.h"
/*
* Exported variables
*/
/*
* Local constants definitions
*/
#define MAX_XLOG_OUTPUTS 10
/*
* Local structures, typedefs and macros
*/
#ifdef UNUSED
#undef UNUSED
#endif
#define UNUSED(x) (x) = (x)
/*
* Local variables
*/
static int init_flag = 0;
static int start_flag = 0;
static pid_t pid = 0;
static char *preamble_string = NULL;
static char *process_name_string = NULL;
static FILE *xlog_outputs_file[MAX_XLOG_OUTPUTS];
static xlog_output_func_t xlog_outputs_func[MAX_XLOG_OUTPUTS];
static void *xlog_outputs_obj[MAX_XLOG_OUTPUTS];
static size_t xlog_output_file_count = 0;
static size_t xlog_output_func_count = 0;
static FILE *fp_default = NULL;
static int xlog_level_enabled[XLOG_LEVEL_MAX];
static xlog_verbose_t xlog_verbose_level[XLOG_LEVEL_MAX];
/*
* XXX: the log level names below has to be consistent with the XLOG_LEVEL_*
* values.
*/
static const char *xlog_level_names[XLOG_LEVEL_MAX] = {
"FATAL",
"ERROR",
"WARNING",
"INFO",
"TRACE"
};
/* State and functions for systems without varargs preprocessors */
static const char *the_module = NULL;
static const char *the_file = NULL;
static int the_line = 0;
static const char *the_func = NULL;
/*
* Local functions prototypes
*/
static int xlog_init_lock(void);
static int xlog_init_unlock(void);
static int xlog_exit_lock(void);
static int xlog_exit_unlock(void);
static int xlog_write_lock(void);
static int xlog_write_unlock(void);
static void xlog_record_va(xlog_level_t log_level, const char* module_name,
const char *where, const char* format,
va_list ap);
static int xlog_write(FILE* fp, const char* fmt, ...);
static int xlog_write_va(FILE* fp, const char* fmt, va_list ap);
static int xlog_flush(FILE* fp);
static const char* xlog_localtime2string_short(void);
/*
* ****************************************************************************
* Control functions to init/exit/start/stop/etc the log utility.
* ****************************************************************************
*/
/**
* xlog_init:
* @argv0 The path to the executable.
* @preamble_message A string that will become part of the preamble string.
*
* Initialize the log utility.
* As part of the initialization, the preamble string will be set to
* <@module_name><@preamble_message>
*
* Return value: 0 on success, otherwise -1.
**/
int
xlog_init(const char *argv0, const char *preamble_message)
{
const char* process_name;
xlog_level_t level;
if (init_flag)
return (-1);
/* Get the init lock */
if (xlog_init_lock() < 0) {
fprintf(stderr, "Error obtaining xlog_init_lock()\n");
exit (1);
}
pid = getpid();
if (process_name_string != NULL) {
free(process_name_string);
process_name_string = NULL;
}
process_name = strrchr(argv0, '/');
if (process_name != NULL)
process_name++; /* Skip the last '/' */
if (process_name == NULL)
process_name = argv0;
if (process_name != NULL)
process_name_string = strdup(process_name);
/* Set the preamble string */
xlog_set_preamble(preamble_message);
/* Enable all log messages by default, and set default verbose level */
for (level = XLOG_LEVEL_MIN; level < XLOG_LEVEL_MAX; level++) {
xlog_enable(level);
xlog_verbose_level[level] = XLOG_VERBOSE_LOW; /* Default */
}
xlog_verbose_level[XLOG_LEVEL_FATAL] = XLOG_VERBOSE_HIGH; /* XXX */
init_flag = 1;
/* Release the init lock */
xlog_init_unlock();
return (0);
}
/**
* xlog_exit:
* @void:
*
* Gracefully exit logging.
*
* Return value: 0 on success, othewise -1.
**/
int
xlog_exit(void)
{
int i;
xlog_level_t level;
if (! init_flag)
return (-1);
if (start_flag)
xlog_stop();
/* Get the exit lock */
if (xlog_exit_lock() < 0) {
return (-1);
}
/* Reset local variables */
init_flag = 0;
pid = 0;
if (process_name_string != NULL) {
free (process_name_string);
process_name_string = NULL;
}
if (preamble_string != NULL) {
free (preamble_string);
preamble_string = NULL;
}
for (i = 0; i < MAX_XLOG_OUTPUTS; i++) {
xlog_outputs_file[i] = NULL;
xlog_outputs_func[i] = NULL;
xlog_outputs_obj[i] = NULL;
}
xlog_output_file_count = 0;
xlog_output_func_count = 0;
fp_default = 0;
for (level = XLOG_LEVEL_MIN; level < XLOG_LEVEL_MAX; level++) {
xlog_disable(level);
xlog_verbose_level[level] = XLOG_VERBOSE_LOW; /* Default */
}
xlog_verbose_level[XLOG_LEVEL_FATAL] = XLOG_VERBOSE_HIGH; /* XXX */
/* Release the exit lock */
xlog_exit_unlock();
return (0);
}
/**
* xlog_start:
* @void:
*
* Start logging.
*
* Return value: 0 on success, otherwise -1.
**/
int
xlog_start(void)
{
if (! init_flag)
return (-1);
if (start_flag)
return (-1);
start_flag = 1;
return (0);
}
/**
* xlog_stop:
* @void:
*
* Stop logging.
*
* Return value: 0 on success, otherwise -1.
**/
int
xlog_stop(void)
{
if (! start_flag)
return (-1);
start_flag = 0;
return (0);
}
int
xlog_is_running(void)
{
return start_flag;
}
/**
* xlog_enable:
* @log_level: The message type (e.g., %XLOG_LEVEL_WARNING) to enable
* the logging for.
*
* Enable logging for messages of type &log_level_t.
* By default, all message types are enabled.
*
* Return value: 0 on success, otherwise -1.
**/
int
xlog_enable(xlog_level_t log_level)
{
if (XLOG_LEVEL_MAX <= log_level)
return (-1);
xlog_level_enabled[log_level] = 1;
return (0);
}
/**
* xlog_disable:
* @log_level: The message type (e.g., %XLOG_LEVEL_WARNING) to disable
* the logging for.
*
* Enable logging for messages of type &log_level_t.
* XXX: %XLOG_LEVEL_FATAL cannot be disabled.
*
* Return value: 0 on success, otherwise -1.
**/
int
xlog_disable(xlog_level_t log_level)
{
if (XLOG_LEVEL_MAX <= log_level)
return (-1);
/* XXX: XLOG_LEVEL_FATAL can never be disabled */
if (log_level == XLOG_LEVEL_FATAL)
return (-1);
xlog_level_enabled[log_level] = 0;
return (0);
}
/**
* xlog_set_preamble:
* @text: The preamble string, or NULL if no preamble.
*
* Set the preamble string for the log entries.
**/
void
xlog_set_preamble(const char* text)
{
/* Free the memory for the old preamble */
if (preamble_string != NULL) {
free(preamble_string);
preamble_string = NULL;
}
/* Duplicate the new preamble string */
if (text != NULL) {
preamble_string = strdup(text);
}
}
/**
* xlog_process_name:
*
* Get the process name as set by xlog_init.
* Return value: pointer to process name on success, NULL otherwise.
**/
const char*
xlog_process_name()
{
return process_name_string;
}
/**
* xlog_set_verbose:
* @verbose_level: The level of verbosity #xlog_verbose_t
* (higher is more verbose).
*
* Set the level of verbosity (#xlog_verbose_t) for the log entries.
* Applies for all type of messages except for %XLOG_LEVEL_FATAL
* which always is set to the most verbose level.
**/
void
xlog_set_verbose(xlog_verbose_t verbose_level)
{
int i;
if (XLOG_VERBOSE_HIGH < verbose_level)
verbose_level = XLOG_VERBOSE_HIGH;
for (i = 0; i < XLOG_LEVEL_MAX; i++) {
if (i == XLOG_LEVEL_FATAL)
continue; /* XXX: XLOG_LEVEL_FATAL cannot be changed */
xlog_verbose_level[i] = verbose_level;
}
}
/**
* xlog_level_set_verbose:
* @log_level: The message type #xlog_level_t to set the verbosity of.
* @verbose_level: The level of verbosity #xlog_verbose_t
* (higher is more verbose).
*
* Set the level of verbosity (#xlog_verbose_t) for the log entries
* of messages of type #xlog_level_t.
* Note: %XLOG_LEVEL_FATAL verbosity cannot be changed, and is
* always set to the most verbose level.
**/
void
xlog_level_set_verbose(xlog_level_t log_level, xlog_verbose_t verbose_level)
{
if (XLOG_LEVEL_MAX <= log_level)
return;
if (log_level == XLOG_LEVEL_FATAL)
return; /* XXX: XLOG_LEVEL_FATAL cannot be changed */
if (XLOG_VERBOSE_HIGH < verbose_level)
verbose_level = XLOG_VERBOSE_HIGH;
xlog_verbose_level[log_level] = verbose_level;
}
/*
* Macro function to generate xlog_info, xlog_warning, etc...
*/
#define xlog_fn(fn, log_level) \
void \
xlog_##fn (const char *module_name, const char *where, const char *fmt, ...) \
{ \
va_list ap; \
va_start(ap, fmt); \
xlog_record_va(log_level, module_name, where, fmt, ap); \
va_end(ap); \
}
/*
* Macro function to generate a log function that aborts. E.g.: xlog_fatal
* XXX: if signal SIGABRT is caught and the signal handler does not return,
* the program will NOT terminate.
*/
#define xlog_fn_abort(fn, log_level) \
void \
xlog_##fn (const char *module_name, const char *where, const char *fmt, ...) \
{ \
va_list ap; \
va_start(ap, fmt); \
xlog_record_va(log_level, module_name, where, fmt, ap); \
va_end(ap); \
abort(); \
}
/*
* Generate the log functions
*/
xlog_fn_abort(fatal, XLOG_LEVEL_FATAL)
xlog_fn(error, XLOG_LEVEL_ERROR)
xlog_fn(warning, XLOG_LEVEL_WARNING)
xlog_fn(info, XLOG_LEVEL_INFO)
/*
* ****************************************************************************
* Lock/unlock functions
*
* TODO: not implemented yet. Add whatever locks may be needed.
* ****************************************************************************
*/
/**
* xlog_init_lock:
* @void:
*
* Obtain a lock needed during initialization of the log utility.
*
* Return value: 0 on success, otherwise -1.
**/
static int
xlog_init_lock(void)
{
return (0);
}
/**
* xlog_init_unlock:
* @void:
*
* Release the lock used during initialization of the log utility.
*
* Return value: 0 on success, otherwise -1.
**/
static int
xlog_init_unlock(void)
{
return (0);
}
/**
* xlog_exit_lock:
* @void:
*
* Obtain a lock needed during exit from the log utility.
*
* Return value: 0 on success, otherwise -1.
**/
static int
xlog_exit_lock(void)
{
return (0);
}
/**
* xlog_exit_unlock:
* @void:
*
* Release the lock used during exit from the log utility.
*
* Return value:
**/
static int
xlog_exit_unlock(void)
{
return (0);
}
/**
* xlog_write_lock:
* @void:
*
* Obtain a lock needed during writing of a log message.
*
* Return value: 0 on success, otherwise -1.
**/
static int
xlog_write_lock(void)
{
return (0);
}
/**
* xlog_write_unlock:
* @void:
*
* Release the lock used during writing of a log message.
*
* Return value: 0 on success, otherwise -1.
**/
static int
xlog_write_unlock(void)
{
return (0);
}
/*
* ****************************************************************************
* Print functions
* ****************************************************************************
*/
/**
* xlog_record_va:
* @log_level: The log level of the message to write.
* @module_name: The name of the module this message applies to.
* @where: Where the log was generated (e.g., the file name, line number, etc).
* @format: The printf() format of the message to write.
* Note that a trailing newline is added if none is present.
* @ap: The vararg list of arguments.
*
* Write a log message.
**/
static void
xlog_record_va(xlog_level_t log_level, const char *module_name,
const char *where, const char *format, va_list ap)
{
const char *preamble_lead;
const char *process_name_lead;
char *buf_payload_ptr = NULL, *buf_preamble_ptr = NULL;
char *buf_output_ptr = NULL;
int buf_output_size;
size_t i;
#ifdef SIGPIPE
sig_t sigpipe_handler;
#endif
if (! start_flag) {
if (! init_flag)
fprintf(stderr,
"Logging must be initialized first by xlog_init()\n");
if (! start_flag)
fprintf(stderr,
"Logging must be started first by xlog_start()\n");
abort();
}
if ((xlog_output_file_count == 0) && (xlog_output_func_count == 0))
return;
if (XLOG_LEVEL_MAX <= log_level)
return; /* Invalid log level */
if (! xlog_level_enabled[log_level])
return; /* The log level is disabled */
xlog_write_lock();
#ifdef SIGPIPE
sigpipe_handler = signal(SIGPIPE, SIG_IGN);
#endif
preamble_lead = (preamble_string) ? preamble_string : "";
process_name_lead = (process_name_string) ? process_name_string : "";
/*
* Prepare the preamble string to write.
* XXX: we need to prepare it once, otherwise the time may be
* different when we write to more than one outputs.
*/
switch (xlog_verbose_level[log_level]) {
case XLOG_VERBOSE_LOW: /* The minimum log information */
x_asprintf(&buf_preamble_ptr, "[ %s %s %s %s ] ",
xlog_localtime2string_short(),
xlog_level_names[log_level],
process_name_lead,
module_name);
break;
case XLOG_VERBOSE_MEDIUM: /* Add preamble string if non-NULL */
x_asprintf(&buf_preamble_ptr, "[ %s %s %s %s %s ] ",
xlog_localtime2string_short(),
preamble_lead,
xlog_level_names[log_level],
process_name_lead,
module_name);
break;
case XLOG_VERBOSE_HIGH: /* Most verbose */
default:
x_asprintf(&buf_preamble_ptr, "[ %s %s %s %s:%d %s %s ] ",
xlog_localtime2string_short(),
preamble_lead,
xlog_level_names[log_level],
process_name_lead,
(int)pid,
module_name,
where);
break;
}
/*
* Prepare the payload string to write
*/
x_vasprintf(&buf_payload_ptr, format, ap);
if ((buf_preamble_ptr == NULL)
&& ((buf_payload_ptr == NULL) || buf_payload_ptr[0] == '\0'))
goto cleanup_label;
/*
* Prepare the output string to write.
* XXX: here we explicitly add the '\n' at the end of the
* output string, because the XLOG message format implies it.
*/
buf_output_size = x_asprintf(&buf_output_ptr, "%s%s\n",
buf_preamble_ptr, buf_payload_ptr);
if ((buf_output_ptr == NULL)
|| (buf_output_ptr[0] == '\0')
|| (buf_output_size < 0)) {
goto cleanup_label;
}
/* Remove our '\n' from the end if the payload itself already has one */
if (buf_output_size >= 2) {
char n1, n2;
n1 = buf_output_ptr[buf_output_size - 2];
n2 = buf_output_ptr[buf_output_size - 1];
if ((n1 == '\n') && (n2 == '\n'))
buf_output_ptr[buf_output_size - 1] = '\0';
}
/*
* Write to the file descriptors
*/
for (i = 0; i < xlog_output_file_count; ) {
FILE *fp = xlog_outputs_file[i];
if (xlog_write(fp, "%s", buf_output_ptr) || xlog_flush(fp)) {
xlog_remove_output(fp);
continue;
}
i++;
}
/*
* Write to the functions
*/
for (i = 0; i < xlog_output_func_count; ) {
xlog_output_func_t func = xlog_outputs_func[i];
void *obj = xlog_outputs_obj[i];
if (func(obj, buf_output_ptr) < 0) {
xlog_remove_output_func(func, obj);
continue;
}
i++;
}
cleanup_label:
/*
* Cleanup
*/
if (buf_preamble_ptr)
free(buf_preamble_ptr);
if (buf_payload_ptr)
free(buf_payload_ptr);
if (buf_output_ptr)
free(buf_output_ptr);
#ifdef SIGPIPE
signal(SIGPIPE, sigpipe_handler);
#endif
xlog_write_unlock();
}
/**
* xlog_write:
* @fp: The file descriptor to write to.
* @fmt: The printf() style format of the message to write.
* @:
*
* Format a message for writing and call a function to write it to @fp.
*
* Return value: 0 on success, otherwise -1.
**/
static int
xlog_write(FILE* fp, const char* fmt, ...)
{
va_list ap;
int retval;
va_start(ap, fmt);
retval = xlog_write_va(fp, fmt, ap);
va_end(ap);
return (retval);
}
/**
* xlog_write_va:
* @fp: The file descriptor to write to.
* @fmt: The printf() style format of the message to write.
* @ap: The vararg list of arguments.
*
* Write a message to @fp.
*
* Return value: 0 on success, otherwise -1.
**/
static int
xlog_write_va(FILE* fp, const char* fmt, va_list ap)
{
assert(fp != NULL);
if (vfprintf(fp, fmt, ap) < 0) {
return (-1);
}
return (0);
}
/**
* xlog_flush:
* @fp: The file descriptor to flush.
*
* Flushes all buffered data for output stream @fp.
*
* Return value: 0 on success, otherwise -1.
**/
static int
xlog_flush(FILE* fp)
{
if (! fflush(fp))
return (0);
else
return (-1);
}
/**
* _xcond_trace_msg_long:
* @module_name: The name of the module this message applies to.
* @file: The file name where XLOG_TRACE() has occured.
* @line: The line number where XLOG_TRACE() has occured.
* @func: The function name where XLOG_TRACE() has occured.
* @flag: The boolean flag that defines whether the messages should be printed.
* @fmt: The printf() style format.
* @:
*
* The entry function for XLOG_TRACE(). Used if the compiler supports
* variable arguments for macros.
* This function should not be called directly.
**/
void
_xcond_trace_msg_long(const char *module_name,
const char *file,
int line,
const char *func,
int flag,
const char *fmt, ...)
{
if (flag) {
va_list ap;
char xlog_where_buf[8000];
/* TODO: use _xcond_trace_entry and _xcond_trace_msg_short instead */
sprintf(xlog_where_buf, "+%d %s %s", line, file,
(func) ? func : "(unknown_func)");
va_start(ap, fmt);
xlog_record_va(XLOG_LEVEL_TRACE, module_name, xlog_where_buf, fmt, ap);
va_end(ap);
}
}
/**
* _xcond_trace_entry:
* @module_name: The name of the module this message applies to.
* @file: The file name where XLOG_TRACE() has occured.
* @line: The line number where XLOG_TRACE() has occured.
* @func: The function name where XLOG_TRACE() has occured.
*
* A helper function that sets the file name, line number and function name.
* Needed if the compiler does not support variable arguments for macros.
* This function should not be called directly.
**/
void
_xcond_trace_entry(const char *module_name, const char *file,
int line, const char* func)
{
the_module = module_name;
the_file = file;
the_line = line;
the_func = func;
}
/**
* _xcond_trace_msg_short:
* @flag: The boolean flag that defines whether the messages should be printed.
* @fmt: The printf() style format.
* @:
*
* The entry function for XLOG_TRACE(). Used if the compiler does not support
* variable arguments for macros.
* This function should not be called directly.
**/
void
_xcond_trace_msg_short(int flag, const char* fmt, ...)
{
if (flag) {
va_list ap;
char xlog_where_buf[8000];
sprintf(xlog_where_buf, "+%d %s %s", the_line, the_file,
(the_func) ? the_func : "(unknown_func)");
va_start(ap, fmt);
xlog_record_va(XLOG_LEVEL_TRACE, the_module, xlog_where_buf, fmt, ap);
va_end(ap);
}
}
/*
* ****************************************************************************
* Output stream functions
* ****************************************************************************
*/
/**
* xlog_add_output:
* @fp: The file descriptor to add to the set of output streams.
*
* Add a file descriptor to the set of output streams.
*
* Return value: 0 on success, otherwise -1.
**/
int
xlog_add_output(FILE* fp)
{
size_t i;
for (i = 0; i < xlog_output_file_count; i++) {
if (xlog_outputs_file[i] == fp) {
return (0);
}
}
if (i < MAX_XLOG_OUTPUTS) {
xlog_outputs_file[i] = fp;
xlog_output_file_count++;
return (0);
}
return (-1);
}
/**
* xlog_remove_output:
* @fp: The file descriptor to remove from the set of output streams.
*
* Remove a file descriptor from the set of output streams.
*
* Return value: 0 on success, otherwise -1.
**/
int
xlog_remove_output(FILE* fp)
{
size_t i, j;
for (i = 0; i < xlog_output_file_count; i++) {
if (xlog_outputs_file[i] == fp) {
for(j = i + 1; j < xlog_output_file_count; j++) {
xlog_outputs_file[j - 1] = xlog_outputs_file[j];
}
xlog_output_file_count--;
return (0);
}
}
return (-1);
}
/**
* xlog_add_output_func:
* @func: The function to add to the set of output streams.
* @obj: The object to supply @func with when called.
*
* Add a processing function and an object to the set of output streams.
*
* Return value: 0 on success, otherwise -1.
**/
int
xlog_add_output_func(xlog_output_func_t func, void *obj)
{
size_t i;
for (i = 0; i < xlog_output_func_count; i++) {
if ((xlog_outputs_func[i] == func)
&& (xlog_outputs_obj[i] == obj)) {
return (0);
}
}
if (i < MAX_XLOG_OUTPUTS) {
xlog_outputs_func[i] = func;
xlog_outputs_obj[i] = obj;
xlog_output_func_count++;
return (0);
}
return (-1);
}
/**
* xlog_remove_output_func:
* @func: The function to remove from the set of output streams.
* @obj: The object that @func was supplied with.
*
* Remove a processing function and an object from the set of output streams.
*
* Return value: 0 on success, otherwise -1.
**/
int
xlog_remove_output_func(xlog_output_func_t func, void *obj)
{
size_t i, j;
for (i = 0; i < xlog_output_func_count; i++) {
if ((xlog_outputs_func[i] == func)
&& (xlog_outputs_obj[i] == obj)) {
for(j = i + 1; j < xlog_output_func_count; j++) {
xlog_outputs_func[j - 1] = xlog_outputs_func[j];
xlog_outputs_obj[j - 1] = xlog_outputs_obj[j];
}
xlog_output_func_count--;
return (0);
}
}
return (-1);
}
/**
* xlog_add_default_output:
* @void:
*
* Add the default output stream to the set of output streams.
* XXX: right now the default is '/dev/stderr', but it should eventually be:
* `/dev/console' if the process has sufficient permissions,
* and `/dev/stderr' otherwise.
*
* Return value: 0 on success, otherwise -1.
**/
int
xlog_add_default_output(void)
{
#ifdef HOST_OS_WINDOWS
FILE *fp = NULL;
HANDLE hstd = INVALID_HANDLE_VALUE;
/*
* XXX: We may need to duplicate these handles;
* fclose() on _open_osfhandle() derived handles results
* in the underlying handle being closed.
*/
hstd = GetStdHandle(STD_ERROR_HANDLE);
fp = _fdopen(_open_osfhandle((long)hstd, _O_TEXT), "w");
if (fp != NULL)
return (xlog_add_output(fp));
fp = fopen("CONOUT$", "w");
if (fp != NULL)
return (xlog_add_output(fp));
hstd = GetStdHandle(STD_OUTPUT_HANDLE),
fp = _fdopen(_open_osfhandle((long)hstd, _O_TEXT), "w");
if (fp != NULL)
return (xlog_add_output(fp));
#else /* !HOST_OS_WINDOWS */
const char* defaults[] = { /* The default outputs (in preference order) */
"/dev/stderr", /* XXX: temporary this is the default */
"/dev/console",
"/dev/stdout"
};
size_t ndefaults = sizeof(defaults) / sizeof(defaults[0]);
size_t i;
/*
* Attempt to open default output stream, console first in case
* we are root, then stderr.
*/
for (i = 0; fp_default == NULL && i < ndefaults; i++) {
if ((fp_default = fopen(defaults[i], "w")) != NULL) {
return (xlog_add_output(fp_default));
}
}
#endif /* HOST_OS_WINDOWS */
return -1;
}
/**
* xlog_remove_default_output:
* @void:
*
* Remove the default output stream from the set of output streams.
*
* Return value: 0 on success, otherwise -1.
**/
int
xlog_remove_default_output(void)
{
int r = 0;
if (fp_default != NULL) {
r = xlog_remove_output(fp_default);
fclose (fp_default);
fp_default = NULL;
}
return (r);
}
/**
* xlog_localtime2string_short:
* @void:
*
* Compute the current local time and return it as a string in the format:
* Year/Month/Day Hour:Minute:Second
* Example: 2002/02/05 20:22:09
* Note that the returned string uses statically allocated memory,
* and does not need to be de-allocated.
*
* Return value: A statically allocated string with the local time using
* the format described above.
**/
static const char*
xlog_localtime2string_short(void)
{
static char buf[64];
static size_t buf_bytes = sizeof(buf) / sizeof(buf[0]);
time_t now = time(NULL);
struct tm *timeptr = localtime(&now);
strftime(buf, buf_bytes, "%Y/%m/%d %H:%M:%S", timeptr);
return (buf);
}
/**
* xlog_localtime2string:
* @void:
*
* Compute the current local time and return it as a string in the format:
* Year/Month/Day Hour:Minute:Second.Microsecond
* Example: 2002/02/05 20:22:09.808632
* Note that the returned string uses statically allocated memory,
* and does not need to be de-allocated.
*
* Return value: A statically allocated string with the local time using
* the format described above.
* XXX: This function uses a BSD-specific function, gettimeofday().
* XXX: This function calls sprintf() without bounds checking.
**/
const char *
xlog_localtime2string(void)
{
#ifdef HOST_OS_WINDOWS
static char buf[sizeof("999999999/99/99 99/99/99.999999999 ")];
SYSTEMTIME st;
DWORD usecs;
GetSystemTime(&st);
usecs = st.wMilliseconds * 1000;
snprintf(buf, sizeof(buf), "%u/%u/%u %u:%u:%u.%lu",
st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond,
usecs);
return (buf);
#else /* !HOST_OS_WINDOWS */
/* XXX: The code below may stop working after year 999999999 */
char buf[sizeof("999999999/99/99 99/99/99.999999999 ")];
static char ret_buf[sizeof(buf)];
struct timeval tv;
time_t clock;
struct tm *tm;
gettimeofday(&tv, NULL);
clock = tv.tv_sec;
tm = localtime(&clock);
if (strftime(buf, sizeof(buf), "%Y/%m/%d %H:%M:%S", tm) == 0) {
sprintf(ret_buf, "strftime ERROR");
return (ret_buf);
}
sprintf(ret_buf, "%s.%lu", buf, (unsigned long)tv.tv_usec);
return (ret_buf);
#endif /* HOST_OS_WINDOWS */
}
/**
* x_vasprintf:
* @ret: A pointer to the string pointer to store the result.
* @format: The printf(3)-style format.
* @ap: The variable arguments for @format.
*
* A local implementation of vasprintf(3).
* Note that vasprintf(3) is not POSIX, so we don't attempt to
* check whether it is available and use the system implementation
* instead of our own below.
* Part of the reasoning for not using it is technical:
* on Linux vasprintf(3) is declared #ifdef __USE_GNU in <stdio.h>,
* and it is not healthy to define __USE_GNU just to make the code
* compile on Linux.
*
* Return value: (From FreeBSD vasprintf(3) manual page).
* The number of characters printed (not including the
* trailing '\0' used to end output to strings). Also, set @*ret to be
* a pointer to a buffer sufficiently large to hold the formatted string.
* This pointer should be passed to free(3) to release the allocated
* storage when it is no longer needed. If sufficient space cannot
* be allocated, will return -1 and set @ret to be a NULL pointer.
*
* TODO: this function and x_asprintf() below probably should
* be renamed to vasprintf() and asprintf() and go somewhere else,
* something like "compat.c" .
**/
int
x_vasprintf(char **ret, const char *format, va_list ap)
{
size_t i, buf_size = 1024 + 1;
char *buf_ptr = NULL;
int ret_size;
#ifdef HAVE_VA_COPY
va_list temp;
#endif
for (i = 0; i < 3; i++) {
/*
* XXX: two iterations should be sufficient to compute the
* buffer size and allocate it, but anyway we try one more time
*/
buf_ptr = malloc(buf_size);
if (buf_ptr == NULL)
break; /* Cannot allocate memory */
buf_ptr[0] = '\0';
/* XXX
* va_copy does not exist in older compilers like gcc
* 2.95. On some architectures like the i386 using a va_list
* argument multiple times doesn't seem to cause a problem. On
* the AMD 64 it is required.
*/
#ifdef HAVE_VA_COPY
va_copy(temp, ap);
ret_size = vsnprintf(buf_ptr, buf_size, format, temp);
#else
ret_size = vsnprintf(buf_ptr, buf_size, format, ap);
#endif
if (ret_size < 0)
break; /* Cannot format the string */
if ((size_t)ret_size < buf_size) {
*ret = buf_ptr;
return (ret_size);
}
/* The allocated buffer was too small. Try again. */
free(buf_ptr);
buf_ptr = NULL;
buf_size = ret_size + 1; /* XXX: include space for trailing '\0' */
}
/*
* Error: cannot format the string or cannot allocate enough memory
*/
if (buf_ptr != NULL)
free(buf_ptr);
*ret = NULL;
return (-1);
}
/**
* x_asprintf:
* @ret: A pointer to the string pointer to store the result.
* @format: The printf(3)-style format.
* @...: The variable arguments for @format.
*
* A local implementation of asprintf(3) if it is missing.
* If vasprintf(3) is available, it is called instead.
*
* Return value: (From FreeBSD asprintf(3) manual page).
* The number of characters printed (not including the
* trailing '\0' used to end output to strings). Also, set @*ret to be
* a pointer to a buffer sufficiently large to hold the formatted string.
* This pointer should be passed to free(3) to release the allocated
* storage when it is no longer needed. If sufficient space cannot
* be allocated, will return -1 and set @ret to be a NULL pointer.
**/
int
x_asprintf(char **ret, const char *format, ...)
{
va_list ap;
int ret_size;
va_start(ap, format);
ret_size = x_vasprintf(ret, format, ap);
va_end(ap);
return (ret_size);
}
syntax highlighted by Code2HTML, v. 0.9.1