First cut of the output streams
This commit was SVN r360.
Этот коммит содержится в:
родитель
904e4f0d58
Коммит
1140112b9b
@ -13,8 +13,8 @@ headers = \
|
||||
argv.h \
|
||||
cmd_line.h \
|
||||
few.h \
|
||||
lam_log.h \
|
||||
malloc.h \
|
||||
output.h \
|
||||
path.h \
|
||||
reactor.h \
|
||||
strncpy.h
|
||||
@ -24,7 +24,7 @@ libutil_la_SOURCES = \
|
||||
argv.c \
|
||||
cmd_line.c \
|
||||
few.c \
|
||||
lam_log.c \
|
||||
output.c \
|
||||
path.c \
|
||||
reactor.c \
|
||||
strncpy.c
|
||||
|
@ -1,81 +0,0 @@
|
||||
/*
|
||||
* $HEADER$
|
||||
*/
|
||||
|
||||
#include "lam/util/lam_log.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#define FILE_LINE_MAX 255
|
||||
static char file_line[FILE_LINE_MAX + 1] = "";
|
||||
|
||||
void _lam_log(FILE *fd, const char *fmt, va_list ap)
|
||||
{
|
||||
/* Write to a file descriptor and the log file */
|
||||
|
||||
if (fd != NULL) {
|
||||
fprintf(fd, file_line);
|
||||
vfprintf(fd, fmt, ap);
|
||||
fflush(fd);
|
||||
}
|
||||
|
||||
file_line[0] = '\0';
|
||||
}
|
||||
|
||||
void _lam_print(FILE * fd, const char *fmt, va_list ap)
|
||||
{
|
||||
/* Write to a file descriptor (usually stdout or stderr) */
|
||||
|
||||
if (fd != NULL) {
|
||||
fprintf(fd, file_line);
|
||||
vfprintf(fd, fmt, ap);
|
||||
fflush(fd);
|
||||
}
|
||||
file_line[0] = '\0';
|
||||
}
|
||||
|
||||
void _lam_set_file_line(const char *name, int line)
|
||||
{
|
||||
sprintf(file_line, "LAM/MPI:%s:%d: ", name, line);
|
||||
}
|
||||
|
||||
void _lam_err(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
_lam_log(stderr, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
void _lam_warn(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
_lam_log(stderr, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void _lam_dbg(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
_lam_log(stdout, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void _lam_exit(int status, const char* fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
if (fmt) {
|
||||
va_start(ap, fmt);
|
||||
if (status != 0) {
|
||||
_lam_log(stderr, fmt, ap);
|
||||
}
|
||||
else {
|
||||
_lam_print(stdout, fmt, ap);
|
||||
}
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
exit(status);
|
||||
}
|
@ -1,55 +0,0 @@
|
||||
/*
|
||||
* $HEADER$
|
||||
*/
|
||||
|
||||
#ifndef LAM_LOG_H
|
||||
#define LAM_LOG_H
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
|
||||
#define lam_dbg(x) \
|
||||
do { \
|
||||
if (OPT_DBG) { \
|
||||
_lam_set_file_line( __FILE__, __LINE__) ; \
|
||||
_lam_dbg x ; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define lam_err(x) \
|
||||
do { \
|
||||
_lam_set_file_line(__FILE__, __LINE__) ; \
|
||||
_lam_err x ; \
|
||||
} while (0)
|
||||
|
||||
#define lam_warn(x) \
|
||||
do { \
|
||||
_lam_set_file_line(__FILE__, __LINE__) ; \
|
||||
_lam_warn x ; \
|
||||
} while (0)
|
||||
|
||||
#define lam_exit(x) \
|
||||
do { \
|
||||
_lam_set_file_line(__FILE__, __LINE__) ; \
|
||||
_lam_exit x ; \
|
||||
} while (0)
|
||||
|
||||
/* Error condition */
|
||||
void _lam_err(const char* fmt, ...);
|
||||
|
||||
/* Warning condition */
|
||||
void _lam_warn(const char* fmt, ...);
|
||||
|
||||
/* Debugging message */
|
||||
void _lam_dbg(const char* fmt, ...);
|
||||
|
||||
/* Exit with error message */
|
||||
void _lam_exit(int status, const char* fmt, ...);
|
||||
|
||||
/* Set file and line info */
|
||||
void _lam_set_file_line(const char *file, int lineno);
|
||||
|
||||
|
||||
#endif /* LAM_LOG_H */
|
||||
|
||||
|
740
src/lam/util/output.c
Обычный файл
740
src/lam/util/output.c
Обычный файл
@ -0,0 +1,740 @@
|
||||
/*
|
||||
* $HEADER$
|
||||
*/
|
||||
|
||||
#include "lam_config.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#if __STDC__
|
||||
#include <stdarg.h>
|
||||
#else
|
||||
#include <varargs.h>
|
||||
#endif
|
||||
#include <stdlib.h>
|
||||
#include <syslog.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "lam/constants.h"
|
||||
#include "lam/util/malloc.h"
|
||||
#include "lam/util/output.h"
|
||||
#include "lam/threads/mutex.h"
|
||||
|
||||
|
||||
/*
|
||||
* Private data
|
||||
*/
|
||||
int verbose_stream = -1;
|
||||
int verbose_level = 0;
|
||||
lam_output_stream_t verbose = {
|
||||
/* debugging */
|
||||
false,
|
||||
/* verbose level */
|
||||
0,
|
||||
/* syslog */
|
||||
false, 0, NULL, NULL,
|
||||
/* stdout */
|
||||
false,
|
||||
/* stderr */
|
||||
true,
|
||||
/* file */
|
||||
false, false, NULL
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Private functions
|
||||
*/
|
||||
static void output(int output_id, char *format, va_list arglist);
|
||||
static char *lam_vsnprintf(char *format, va_list arglist);
|
||||
|
||||
|
||||
/*
|
||||
* Internal data structures and helpers for the generalized output
|
||||
* stream mechanism.
|
||||
*/
|
||||
struct output_desc_t {
|
||||
bool ldi_used;
|
||||
bool ldi_enabled;
|
||||
int ldi_verbose_level;
|
||||
|
||||
bool ldi_syslog;
|
||||
int ldi_syslog_priority;
|
||||
char *ldi_syslog_ident;
|
||||
|
||||
char *ldi_prefix;
|
||||
int ldi_prefix_len;
|
||||
|
||||
bool ldi_stdout;
|
||||
bool ldi_stderr;
|
||||
|
||||
int ldi_fd;
|
||||
char *ldi_file_suffix;
|
||||
};
|
||||
typedef struct output_desc_t output_desc_t;
|
||||
|
||||
#define LAM_OUTPUT_MAX_STREAMS 32
|
||||
|
||||
|
||||
/*
|
||||
* Local state
|
||||
*/
|
||||
static bool initialized = false;
|
||||
static output_desc_t info[LAM_OUTPUT_MAX_STREAMS];
|
||||
static char *temp_str = 0;
|
||||
static int temp_str_len = 0;
|
||||
static lam_mutex_t mutex;
|
||||
|
||||
|
||||
/**
|
||||
* Initializes the output stream system and opens a default
|
||||
* "verbose" stream.
|
||||
*
|
||||
* @retval true Upon success.
|
||||
* @retval false Upon failure.
|
||||
*
|
||||
* This should be the first function invoked in the output subsystem.
|
||||
* After this call, the default "verbose" stream is open and can be
|
||||
* written to via calls to lam_output_verbose() and
|
||||
* lam_output_error().
|
||||
*
|
||||
* By definition, the default verbose stream has a handle ID of 0, and
|
||||
* has a verbose level of 0.
|
||||
*/
|
||||
bool lam_output_init(void)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < LAM_OUTPUT_MAX_STREAMS; ++i) {
|
||||
info[i].ldi_used = false;
|
||||
info[i].ldi_enabled = false;
|
||||
|
||||
info[i].ldi_syslog = false;
|
||||
info[i].ldi_fd = -1;
|
||||
}
|
||||
|
||||
/* Initialize the mutex that protects the output */
|
||||
|
||||
lam_mutex_init(&mutex);
|
||||
initialized = true;
|
||||
|
||||
/* Open the default verbose stream */
|
||||
|
||||
verbose_stream = lam_output_open(&verbose);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Opens output streams.
|
||||
*
|
||||
* @param lds A pointer to lam_output_stream_t describing what the
|
||||
* characteristics of the output stream should be.
|
||||
*
|
||||
* This function opens an output stream and returns an integer handle.
|
||||
* The caller is responsible for maintaining the handle and using it
|
||||
* in successive calls to LAM_OUTPUT(), lam_output(),
|
||||
* lam_output_switch(), and lam_output_close().
|
||||
*
|
||||
* It is safe to have multiple threads invoke this function
|
||||
* simultaneously; their execution will be serialized in an
|
||||
* unspecified manner.
|
||||
*/
|
||||
int lam_output_open(lam_output_stream_t *lds)
|
||||
{
|
||||
int i;
|
||||
int flags;
|
||||
char *filename;
|
||||
|
||||
/* Setup */
|
||||
|
||||
if (!initialized)
|
||||
lam_output_init();
|
||||
|
||||
/* Find an available stream, or return LAM_ERROR */
|
||||
|
||||
THREAD_LOCK(&mutex);
|
||||
for (i = 0; i < LAM_OUTPUT_MAX_STREAMS; ++i)
|
||||
if (!info[i].ldi_used)
|
||||
break;
|
||||
if (i >= LAM_OUTPUT_MAX_STREAMS) {
|
||||
THREAD_UNLOCK(&mutex);
|
||||
return LAM_ERR_OUT_OF_RESOURCE;
|
||||
}
|
||||
|
||||
/* Got a stream -- now initialize it and open relevant outputs */
|
||||
|
||||
info[i].ldi_used = true;
|
||||
THREAD_UNLOCK(&mutex);
|
||||
info[i].ldi_enabled = lds->lds_is_debugging ? (bool) LAM_ENABLE_DEBUG : true;
|
||||
info[i].ldi_verbose_level = 0;
|
||||
|
||||
info[i].ldi_syslog = lds->lds_want_syslog;
|
||||
if (lds->lds_want_syslog) {
|
||||
if (NULL != lds->lds_syslog_ident) {
|
||||
info[i].ldi_syslog_ident = strdup(lds->lds_syslog_ident);
|
||||
openlog(lds->lds_syslog_ident, LOG_PID, LOG_USER);
|
||||
} else {
|
||||
info[i].ldi_syslog_ident = NULL;
|
||||
openlog("lam", LOG_PID, LOG_USER);
|
||||
}
|
||||
info[i].ldi_syslog_priority = lds->lds_syslog_priority;
|
||||
}
|
||||
|
||||
if (NULL != lds->lds_prefix) {
|
||||
info[i].ldi_prefix = strdup(lds->lds_prefix);
|
||||
info[i].ldi_prefix_len = strlen(lds->lds_prefix);
|
||||
} else {
|
||||
info[i].ldi_prefix = NULL;
|
||||
info[i].ldi_prefix_len = 0;
|
||||
}
|
||||
|
||||
info[i].ldi_stdout = lds->lds_want_stdout;
|
||||
info[i].ldi_stderr = lds->lds_want_stderr;
|
||||
|
||||
info[i].ldi_fd = -1;
|
||||
if (lds->lds_want_file) {
|
||||
|
||||
/* Setup the filename and open flags */
|
||||
|
||||
#if NEED_TO_IMPLEMENT_SESSION_DIRECTORY
|
||||
filename = lam_get_tmpdir();
|
||||
#else
|
||||
filename = LAM_MALLOC(256);
|
||||
strcpy(filename, "/tmp");
|
||||
#endif
|
||||
strcat(filename, "/lam-");
|
||||
if (lds->lds_file_suffix != NULL) {
|
||||
info[i].ldi_file_suffix = strdup(lds->lds_file_suffix);
|
||||
strcat(filename, lds->lds_file_suffix);
|
||||
} else {
|
||||
info[i].ldi_file_suffix = NULL;
|
||||
strcat(filename, "output.txt");
|
||||
}
|
||||
flags = O_CREAT | O_RDWR;
|
||||
if (!lds->lds_want_file_append)
|
||||
flags |= O_TRUNC;
|
||||
|
||||
/* Actually open the file */
|
||||
|
||||
info[i].ldi_fd = open(filename, flags, 0644);
|
||||
if (-1 == info[i].ldi_fd) {
|
||||
info[i].ldi_used = false;
|
||||
return LAM_ERR_IN_ERRNO;
|
||||
}
|
||||
|
||||
/* Make the file be close-on-exec to prevent child inheritance
|
||||
problems */
|
||||
|
||||
fcntl(info[i].ldi_fd, F_SETFD, 1);
|
||||
LAM_FREE(filename);
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Enables and disables output streams.
|
||||
*
|
||||
* @param output_id Stream handle to switch
|
||||
* @param enable Boolean indicating whether to enable the stream
|
||||
* output or not.
|
||||
*
|
||||
* @returns The previous enable state of the stream (true == enabled,
|
||||
* false == disabled).
|
||||
*
|
||||
* The output of a stream can be temporarily disabled by passing an
|
||||
* enable value to false, and later resumed by passing an enable value
|
||||
* of true. This does not close the stream -- it simply tells the
|
||||
* lam_output subsystem to intercept and discard any output sent to
|
||||
* the stream via LAM_OUTPUT() or lam_output() until the output is
|
||||
* re-enabled.
|
||||
*/
|
||||
bool lam_output_switch(int output_id, bool enable)
|
||||
{
|
||||
bool ret = false;
|
||||
|
||||
/* Setup */
|
||||
|
||||
if (!initialized)
|
||||
lam_output_init();
|
||||
|
||||
if (output_id >= 0 && output_id < LAM_OUTPUT_MAX_STREAMS) {
|
||||
ret = info[output_id].ldi_enabled;
|
||||
info[output_id].ldi_enabled = enable;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \internal
|
||||
*
|
||||
* Reopens all existing output streams.
|
||||
*
|
||||
* This function should never be called by user applications; it is
|
||||
* typically only invoked after a restart (i.e., in a new process)
|
||||
* where output streams need to be re-initialized.
|
||||
*/
|
||||
void lam_output_reopen_all(void)
|
||||
{
|
||||
int i;
|
||||
lam_output_stream_t lds;
|
||||
|
||||
for (i = 0; i < LAM_OUTPUT_MAX_STREAMS; ++i) {
|
||||
|
||||
/* scan till we find ldi_used == 0, which is the end-marker */
|
||||
if (!info[i].ldi_used)
|
||||
break;
|
||||
|
||||
/*
|
||||
* set this to zero to ensure that lam_output_open will return this same
|
||||
* index as the output stream id
|
||||
*/
|
||||
info[i].ldi_used = false;
|
||||
|
||||
lds.lds_want_syslog = info[i].ldi_syslog;
|
||||
lds.lds_syslog_priority = info[i].ldi_syslog_priority;
|
||||
lds.lds_syslog_ident = info[i].ldi_syslog_ident;
|
||||
lds.lds_prefix = info[i].ldi_prefix;
|
||||
lds.lds_want_stdout = info[i].ldi_stdout;
|
||||
lds.lds_want_stderr = info[i].ldi_stderr;
|
||||
lds.lds_want_file = (-1 == info[i].ldi_fd) ? false: true;
|
||||
/* open all streams in append mode */
|
||||
lds.lds_want_file_append = true;
|
||||
lds.lds_file_suffix = info[i].ldi_file_suffix;
|
||||
|
||||
/*
|
||||
* call lam_output_open to open the stream. The return value is
|
||||
* guaranteed to be i. So we can ignore it.
|
||||
*/
|
||||
lam_output_open(&lds);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Close an output stream.
|
||||
*
|
||||
* @param output_id Handle of the stream to close.
|
||||
*
|
||||
* Close an output stream. No output will be sent to the stream after
|
||||
* it is closed. Be aware that output handles tend to be re-used; it
|
||||
* is possible that after a stream is closed, if another stream is
|
||||
* opened, it will get the same handle value.
|
||||
*/
|
||||
void lam_output_close(int output_id)
|
||||
{
|
||||
int i;
|
||||
output_desc_t *ldi;
|
||||
|
||||
/* Setup */
|
||||
|
||||
if (!initialized)
|
||||
lam_output_init();
|
||||
|
||||
/* If it's valid, used, enabled, and has an open file descriptor,
|
||||
close it */
|
||||
|
||||
if (output_id >= 0 && output_id < LAM_OUTPUT_MAX_STREAMS &&
|
||||
info[output_id].ldi_used &&
|
||||
info[output_id].ldi_enabled) {
|
||||
|
||||
ldi = &info[output_id];
|
||||
|
||||
if (-1 != ldi->ldi_fd)
|
||||
close(ldi->ldi_fd);
|
||||
ldi->ldi_used = false;
|
||||
|
||||
/* If we strduped a prefix, suffix, or syslog ident, free it */
|
||||
|
||||
if (NULL != ldi->ldi_prefix)
|
||||
LAM_FREE(ldi->ldi_prefix);
|
||||
ldi->ldi_prefix = NULL;
|
||||
|
||||
if (NULL != ldi->ldi_file_suffix)
|
||||
LAM_FREE(ldi->ldi_file_suffix);
|
||||
ldi->ldi_file_suffix = NULL;
|
||||
|
||||
if (NULL != ldi->ldi_syslog_ident)
|
||||
LAM_FREE(ldi->ldi_syslog_ident);
|
||||
ldi->ldi_syslog_ident = NULL;
|
||||
}
|
||||
|
||||
/* If no one has the syslog open, we should close it */
|
||||
|
||||
THREAD_LOCK(&mutex);
|
||||
for (i = 0; i < LAM_OUTPUT_MAX_STREAMS; ++i)
|
||||
if (info[i].ldi_used && info[i].ldi_syslog)
|
||||
break;
|
||||
if (i >= LAM_OUTPUT_MAX_STREAMS)
|
||||
closelog();
|
||||
|
||||
/* Somewhat of a hack to free up the temp_str */
|
||||
|
||||
if (NULL != temp_str) {
|
||||
LAM_FREE(temp_str);
|
||||
temp_str = NULL;
|
||||
temp_str_len = 0;
|
||||
}
|
||||
THREAD_UNLOCK(&mutex);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Main function to send output to a stream.
|
||||
*
|
||||
* @param output_id Stream id returned from lam_output_open().
|
||||
* @param format printf-style format string.
|
||||
* @param varargs printf-style varargs list to fill the string
|
||||
* specified by the format parameter.
|
||||
*
|
||||
* This is the main function to send output to custom streams (note
|
||||
* that output to the default "verbose" stream is handled through
|
||||
* lam_output_verbose() and lam_output_error()).
|
||||
*
|
||||
* It is never necessary to send a trailing "\n" in the strings to
|
||||
* this function; some streams requires newlines, others do not --
|
||||
* this function will append newlines as necessary.
|
||||
*
|
||||
* Verbosity levels are ignored in this function.
|
||||
*/
|
||||
void lam_output(int output_id, char *format, ...)
|
||||
{
|
||||
va_list arglist;
|
||||
#if __STDC__
|
||||
va_start(arglist, format);
|
||||
#else
|
||||
va_start(arglist);
|
||||
#endif
|
||||
output(output_id, format, arglist);
|
||||
va_end(arglist);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Send output to a stream only if the passed verbosity level is high
|
||||
* enough.
|
||||
*
|
||||
* @param output_id Stream id returned from lam_output_open().
|
||||
* @param level Target verbosity level.
|
||||
* @param format printf-style format string.
|
||||
* @param varargs printf-style varargs list to fill the string
|
||||
* specified by the format parameter.
|
||||
*
|
||||
* Output is only sent to the stream if the current verbosity level is
|
||||
* greater than or equal to the level parameter. This mechanism can
|
||||
* be used to send "information" kinds of output to user applications,
|
||||
* but only when the user has asked for a high enough verbosity level.
|
||||
*
|
||||
* It is never necessary to send a trailing "\n" in the strings to
|
||||
* this function; some streams requires newlines, others do not --
|
||||
* this function will append newlines as necessary.
|
||||
*
|
||||
* This function is really a convenience wrapper around checking the
|
||||
* current verbosity level set on the stream, and if the passed level
|
||||
* is less than or equal to the stream's verbosity level, this
|
||||
* function will effectively invoke lam_output to send the output to
|
||||
* the stream.
|
||||
*
|
||||
* @see lam_output_set_verbosity()
|
||||
*/
|
||||
void lam_output_verbose(int output_id, int level, char *format, ...)
|
||||
{
|
||||
if (info[output_id].ldi_verbose_level >= level) {
|
||||
va_list arglist;
|
||||
#if __STDC__
|
||||
va_start(arglist, format);
|
||||
#else
|
||||
va_start(arglist);
|
||||
#endif
|
||||
output(output_id, format, arglist);
|
||||
va_end(arglist);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the verbosity level for a stream.
|
||||
*
|
||||
* @param output_id Stream id returned from lam_output_open().
|
||||
* @param level New verbosity level
|
||||
*
|
||||
* This function sets the verbosity level on a given stream. It will
|
||||
* be used for all future invocations of lam_output_verbose().
|
||||
*/
|
||||
void lam_output_set_verbosity(int output_id, int level)
|
||||
{
|
||||
info[output_id].ldi_verbose_level = level;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Shut down the output stream system.
|
||||
*
|
||||
* Shut down the output stream system, including the default verbose
|
||||
* stream.
|
||||
*/
|
||||
void lam_output_finalize(void)
|
||||
{
|
||||
if (initialized) {
|
||||
if (verbose_stream != -1)
|
||||
lam_output_close(verbose_stream);
|
||||
verbose_stream = -1;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Do the actual output. Take a va_list so that we can be called from
|
||||
* multiple different places, even functions that took "..." as input
|
||||
* arguments.
|
||||
*/
|
||||
static void output(int output_id, char *format, va_list arglist)
|
||||
{
|
||||
int len, total_len;
|
||||
bool want_newline = false;
|
||||
char *str;
|
||||
output_desc_t *ldi;
|
||||
|
||||
/* Setup */
|
||||
|
||||
if (!initialized)
|
||||
lam_output_init();
|
||||
|
||||
/* If it's valid, used, and enabled, output */
|
||||
|
||||
if (output_id >= 0 && output_id < LAM_OUTPUT_MAX_STREAMS &&
|
||||
info[output_id].ldi_used &&
|
||||
info[output_id].ldi_enabled) {
|
||||
ldi = &info[output_id];
|
||||
|
||||
/* Make the formatted string */
|
||||
|
||||
THREAD_LOCK(&mutex);
|
||||
str = lam_vsnprintf(format, arglist);
|
||||
total_len = len = strlen(str);
|
||||
if ('\n' != str[len - 1]) {
|
||||
want_newline = true;
|
||||
++total_len;
|
||||
}
|
||||
if (NULL != ldi->ldi_prefix)
|
||||
total_len += strlen(ldi->ldi_prefix);
|
||||
if (temp_str_len < total_len + want_newline) {
|
||||
if (NULL != temp_str)
|
||||
LAM_FREE(temp_str);
|
||||
temp_str = malloc(total_len * 2);
|
||||
temp_str_len = total_len * 2;
|
||||
}
|
||||
if (NULL != ldi->ldi_prefix) {
|
||||
if (want_newline)
|
||||
snprintf(temp_str, temp_str_len, "%s%s\n", ldi->ldi_prefix, str);
|
||||
else
|
||||
snprintf(temp_str, temp_str_len, "%s%s", ldi->ldi_prefix, str);
|
||||
} else {
|
||||
if (want_newline)
|
||||
snprintf(temp_str, temp_str_len, "%s\n", str);
|
||||
else
|
||||
snprintf(temp_str, temp_str_len, "%s", str);
|
||||
}
|
||||
|
||||
/* Syslog output */
|
||||
|
||||
if (ldi->ldi_syslog)
|
||||
syslog(ldi->ldi_syslog_priority, str);
|
||||
|
||||
/* stdout output */
|
||||
|
||||
if (ldi->ldi_stdout) {
|
||||
printf(temp_str);
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
/* stderr output */
|
||||
|
||||
if (ldi->ldi_stderr) {
|
||||
fprintf(stderr, temp_str);
|
||||
fflush(stderr);
|
||||
}
|
||||
|
||||
/* File output */
|
||||
|
||||
if (ldi->ldi_fd != -1)
|
||||
write(ldi->ldi_fd, temp_str, total_len);
|
||||
THREAD_UNLOCK(&mutex);
|
||||
|
||||
LAM_FREE(str);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* lam_vsnprintf
|
||||
*
|
||||
* Make a good guess about how long a printf-style varags formatted
|
||||
* string will be once all the % escapes are filled in. We don't
|
||||
* handle every % escape here, but we handle enough, and then add a
|
||||
* fudge factor in at the end. When we have that, alloc out a buffer
|
||||
* and snprintf into it. The whole reason for this routine is because
|
||||
* we can't count on vsnprintf to be on every system. :-( Ok, it's
|
||||
* not exactly the same as vsnprintf, but it's in the same spirit, so
|
||||
* it's ok to use a derrivative of the name...
|
||||
*/
|
||||
static char *lam_vsnprintf(char *format, va_list arglist)
|
||||
{
|
||||
int i, len;
|
||||
char *sarg;
|
||||
int iarg;
|
||||
long larg;
|
||||
double darg;
|
||||
float farg;
|
||||
|
||||
/* Important: keep a copy of the original arglist because when we
|
||||
traverse through the original arglist, it has internal state that
|
||||
knows where it is in the list of args. At the end of this
|
||||
routine, we'll need the whole list in order to call vsprintf(),
|
||||
and there doesn't appear to be a way to "rewind" a va_alist.
|
||||
|
||||
Copy order taken from Autoconf docs
|
||||
*/
|
||||
va_list arglist2;
|
||||
#if LAM_HAVE_VA_COPY
|
||||
va_copy(arglist2, arglist);
|
||||
#elif LAM_HAVE_UNDERSCORE_VA_COPY
|
||||
__va_copy(arglist2, arglist);
|
||||
#else
|
||||
memcpy (&arglist2, &arglist, sizeof(va_list));
|
||||
#endif
|
||||
|
||||
/* Start off with a fudge factor of 128 to handle the % escapes that
|
||||
we aren't calculating here */
|
||||
|
||||
len = strlen(format) + 128;
|
||||
for (i = 0; i < strlen(format); ++i) {
|
||||
if ('%' == format[i] && i + 1 < strlen(format) && '%' != format[i + 1]) {
|
||||
++i;
|
||||
switch(format[i]) {
|
||||
case 's':
|
||||
sarg = va_arg(arglist, char*);
|
||||
/* If there's an arg, get the strlen, otherwise we'll use (null) */
|
||||
if (NULL != sarg)
|
||||
len += strlen(sarg);
|
||||
else
|
||||
len += 5;
|
||||
break;
|
||||
|
||||
case 'd':
|
||||
case 'i':
|
||||
iarg = va_arg(arglist, int);
|
||||
/* Alloc for minus sign */
|
||||
if (iarg < 0)
|
||||
++len;
|
||||
/* Now get the log10 */
|
||||
do {
|
||||
++len;
|
||||
iarg /= 10;
|
||||
} while (0 != iarg);
|
||||
break;
|
||||
|
||||
case 'x':
|
||||
case 'X':
|
||||
iarg = va_arg(arglist, int);
|
||||
/* Now get the log16 */
|
||||
do {
|
||||
++len;
|
||||
iarg /= 16;
|
||||
} while (0 != iarg);
|
||||
break;
|
||||
|
||||
case 'f':
|
||||
farg = va_arg(arglist, int);
|
||||
/* Alloc for minus sign */
|
||||
if (farg < 0) {
|
||||
++len;
|
||||
farg = -farg;
|
||||
}
|
||||
/* Alloc for 3 decimal places + '.' */
|
||||
len += 4;
|
||||
/* Now get the log10 */
|
||||
do {
|
||||
++len;
|
||||
farg /= 10.0;
|
||||
} while (0 != farg);
|
||||
break;
|
||||
|
||||
case 'g':
|
||||
darg = va_arg(arglist, int);
|
||||
/* Alloc for minus sign */
|
||||
if (darg < 0) {
|
||||
++len;
|
||||
darg = -darg;
|
||||
}
|
||||
/* Alloc for 3 decimal places + '.' */
|
||||
len += 4;
|
||||
/* Now get the log10 */
|
||||
do {
|
||||
++len;
|
||||
darg /= 10.0;
|
||||
} while (0 != darg);
|
||||
break;
|
||||
|
||||
case 'l':
|
||||
/* Get %ld %lx %lX %lf */
|
||||
if (i + 1 < strlen(format)) {
|
||||
++i;
|
||||
switch(format[i]) {
|
||||
case 'x':
|
||||
case 'X':
|
||||
larg = va_arg(arglist, int);
|
||||
/* Now get the log16 */
|
||||
do {
|
||||
++len;
|
||||
larg /= 16;
|
||||
} while (0 != larg);
|
||||
break;
|
||||
|
||||
case 'f':
|
||||
darg = va_arg(arglist, int);
|
||||
/* Alloc for minus sign */
|
||||
if (darg < 0) {
|
||||
++len;
|
||||
darg = -darg;
|
||||
}
|
||||
/* Alloc for 3 decimal places + '.' */
|
||||
len += 4;
|
||||
/* Now get the log10 */
|
||||
do {
|
||||
++len;
|
||||
darg /= 10.0;
|
||||
} while (0 != darg);
|
||||
break;
|
||||
|
||||
case 'd':
|
||||
default:
|
||||
larg = va_arg(arglist, int);
|
||||
/* Now get the log10 */
|
||||
do {
|
||||
++len;
|
||||
larg /= 10;
|
||||
} while (0 != larg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Wasn't that simple? Now malloc out a string and do the final
|
||||
formatting into that string. */
|
||||
|
||||
sarg = LAM_MALLOC(len);
|
||||
vsprintf(sarg, format, arglist2);
|
||||
|
||||
/* Return the new string */
|
||||
|
||||
return sarg;
|
||||
}
|
223
src/lam/util/output.h
Обычный файл
223
src/lam/util/output.h
Обычный файл
@ -0,0 +1,223 @@
|
||||
/*
|
||||
* $HEADER$
|
||||
*/
|
||||
|
||||
/** @file
|
||||
* LAM output stream facility.
|
||||
*
|
||||
* The LAM output stream facility is used to send output from the LAM
|
||||
* libraries to output devices. It is meant to fully replace all
|
||||
* forms of printf() (and friends). Output streams are opened via the
|
||||
* lam_output_open() function call, and then sent output via
|
||||
* lam_output_verbose(), LAM_OUTPUT(), and lam_output(). Streams are
|
||||
* closed with lam_output_close().
|
||||
*
|
||||
* Streams can multiplex output to several kinds of outputs (one of
|
||||
* each):
|
||||
*
|
||||
* - the syslog
|
||||
* - standard output
|
||||
* - standard error
|
||||
* - file
|
||||
*
|
||||
* Which outputs to use are specified during lam_output_open().
|
||||
*
|
||||
* lam_output_open() returns an integer handle that is used in
|
||||
* successive calls to LAM_OUTPUT() and lam_output() to send output to
|
||||
* the stream.
|
||||
*
|
||||
* The default "verbose" stream is opened after invoking
|
||||
* lam_output_init() (and closed after invoking
|
||||
* lam_output_finalize()). This stream outputs to stderr only, and
|
||||
* has a stream handle ID of 0.
|
||||
*
|
||||
* It is erroneous to have one thread close a stream and have another
|
||||
* try to write to it. Multiple threads writing to a single stream
|
||||
* will be serialized in an unspecified order.
|
||||
*/
|
||||
|
||||
#ifndef LAM_OUTPUT_H_
|
||||
#define LAM_OUTPUT_H_
|
||||
|
||||
#include "lam_config.h"
|
||||
|
||||
#if __STDC__
|
||||
#include <stdarg.h>
|
||||
#else
|
||||
#ifdef __cplusplus
|
||||
#include <stdarg.h>
|
||||
#else
|
||||
#include <varargs.h>
|
||||
#endif
|
||||
#endif /* __STDC__ */
|
||||
|
||||
|
||||
/**
|
||||
* \class lam_output_stream_t
|
||||
*
|
||||
* Structure used to request the opening of a LAM output stream. A
|
||||
* pointer to this structure is passed to lam_output_open() to tell
|
||||
* the lam_output subsystem where to send output for a given stream.
|
||||
* It is valid to specify multiple destinations of output for a stream
|
||||
* -- output streams can be multiplexed to multiple different
|
||||
* destinations through the lam_output facility.
|
||||
*
|
||||
* Note that all strings in this struct are cached on the stream by
|
||||
* value; there is no need to keep them allocated after the return
|
||||
* from lam_output_open().
|
||||
*
|
||||
* @see output.h
|
||||
*/
|
||||
struct lam_output_stream_t {
|
||||
/**
|
||||
* Indicates whether the output of the stream is
|
||||
* debugging/developer-only output or not.
|
||||
*
|
||||
* This field should be "true" if the output is for debugging
|
||||
* purposes only. In that case, the output will never be sent to
|
||||
* the stream unless LAM was configured with --enable-debug.
|
||||
*/
|
||||
bool lds_is_debugging;
|
||||
|
||||
/**
|
||||
* Indicate the starting verbosity level of the stream.
|
||||
*
|
||||
* Verbose levels are a convenience mechanisms, and are only
|
||||
* consulted when output is sent to a stream through the
|
||||
* lam_output_verbose() function. Verbose levels are ignored in
|
||||
* LAM_OUTPUT() and lam_output().
|
||||
*
|
||||
* Valid verbose levels typically start at 0 (meaning "minimal
|
||||
* information"). Higher verbosity levels generally indicate that
|
||||
* more output and diagnostics should be displayed.
|
||||
*/
|
||||
int lda_verbose_level;
|
||||
|
||||
/**
|
||||
* Indicates whether output of the stream should be sent to the
|
||||
* syslog or not.
|
||||
*
|
||||
* If this field is true, output from this stream is sent to the
|
||||
* syslog, and the following fields are also examined:
|
||||
*
|
||||
* - lds_syslog_priority
|
||||
* - lds_syslog_ident
|
||||
* - lds_prefix
|
||||
*
|
||||
* If this field is false, the above three fields are ignored.
|
||||
*/
|
||||
bool lds_want_syslog;
|
||||
/**
|
||||
* When lam_output_stream_t::lds_want_syslog is true, this field is
|
||||
* examined to see what priority output from the stream should be
|
||||
* sent to the syslog.
|
||||
*
|
||||
* This value should be set as per the syslog(3) man page. It is
|
||||
* typically the OR value of "facilty" and "level" values described
|
||||
* in the man page.
|
||||
*/
|
||||
int lds_syslog_priority;
|
||||
/**
|
||||
* When lam_output_stream_t::lds_want_syslog is true, this field is
|
||||
* examined to see what ident value should be passed to openlog(3).
|
||||
*
|
||||
* If a NULL value is given, the string "lam" is used.
|
||||
*/
|
||||
char *lds_syslog_ident;
|
||||
|
||||
/**
|
||||
* String prefix added to all output on the stream.
|
||||
*
|
||||
* When this field is non-NULL, it is prefixed to all lines of
|
||||
* output on the stream. When this field is NULL, no prefix is
|
||||
* added to each line of output in the stream.
|
||||
*/
|
||||
char *lds_prefix;
|
||||
|
||||
/**
|
||||
* Whether to send stream output to stdout or not.
|
||||
*
|
||||
* If this field is true, stream output is sent to stdout.
|
||||
*/
|
||||
bool lds_want_stdout;
|
||||
/**
|
||||
* Whether to send stream output to stderr or not.
|
||||
*
|
||||
* If this field is true, stream output is sent to stderr.
|
||||
*/
|
||||
bool lds_want_stderr;
|
||||
|
||||
/**
|
||||
* Whether to send stream output to a file or not.
|
||||
*
|
||||
* When this field is true, stream output is sent to a file, and the
|
||||
* following fields are also examined:
|
||||
*
|
||||
* - lds_want_file_append
|
||||
* - lda_file_suffix
|
||||
*/
|
||||
bool lds_want_file;
|
||||
/**
|
||||
* When lam_output_stream_t::lds_want_file is true, this field
|
||||
* indicates whether to append the file (if it exists) or overwrite
|
||||
* it.
|
||||
*
|
||||
* If false, the file is opened with the O_TRUNC flag.
|
||||
*/
|
||||
bool lds_want_file_append;
|
||||
/**
|
||||
* When lam_output_stream_t::lds_want_file is true, this field
|
||||
* indicates the string suffix to add to the filename.
|
||||
*
|
||||
* The output file will be in the LAM session directory and have a
|
||||
* LAM-generated prefix (generally "$sessiondir/lam-"). The suffix
|
||||
* is intended to give stream users a chance to write their output
|
||||
* into unique files. If this field is NULL, the suffix
|
||||
* "output.txt" is used.
|
||||
*/
|
||||
char *lds_file_suffix;
|
||||
};
|
||||
|
||||
typedef struct lam_output_stream_t lam_output_stream_t;
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
bool lam_output_init(void);
|
||||
void lam_output_finalize(void);
|
||||
|
||||
int lam_output_open(lam_output_stream_t *lds);
|
||||
bool lam_output_switch(int output_id, bool enable);
|
||||
void lam_output_reopen_all(void);
|
||||
void lam_output_close(int output_id);
|
||||
|
||||
void lam_output(int output_id, char *format, ...);
|
||||
void lam_output_verbose(int output_id, int verbose_level, char *format, ...);
|
||||
void lam_output_set_verbosity(int output_id, int level);
|
||||
|
||||
#if LAM_ENABLE_DEBUG
|
||||
/**
|
||||
* Main macro for use in sending debugging output to output streams;
|
||||
* will be "compiled out" when LAM is configured without
|
||||
* --enable-debug.
|
||||
*
|
||||
* @see lam_output()
|
||||
*/
|
||||
#define LAM_OUTPUT(a) lam_output a
|
||||
#else
|
||||
/**
|
||||
* Main macro for use in sending debugging output to output streams;
|
||||
* will be "compiled out" when LAM is configured without
|
||||
* --enable-debug.
|
||||
*
|
||||
* @see lam_output()
|
||||
*/
|
||||
#define LAM_OUTPUT(a)
|
||||
#endif
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* LAM_OUTPUT_H_ */
|
||||
|
Загрузка…
x
Ссылка в новой задаче
Block a user