/*
 * Copyright (c) 2003-2012
 * Distributed Systems Software.  All rights reserved.
 * See the file LICENSE for redistribution information.
 *
 * $Id: log.h 2594 2012-10-19 17:28:49Z brachman $
 */

#ifndef _LOG_H_
#define _LOG_H_

#include <sys/types.h>
#include <string.h>
#include <regex.h>
#include <stdarg.h>
#include <stdlib.h>
#include <errno.h>
#include <stdio.h>

#include "dacs_config.h"

/* As for syslog(3) and Apache */
typedef enum {
  LOG_LOWEST_LEVEL   = 0,
  LOG_ALL_LEVEL      = 0,
  LOG_TRACE_LEVEL    = 0,
  LOG_DEBUG_LEVEL    = 1,
  LOG_INFO_LEVEL     = 2,
  LOG_NOTICE_LEVEL   = 3,
  LOG_WARN_LEVEL     = 4,
  LOG_ERROR_LEVEL    = 5,
  LOG_CRITICAL_LEVEL = 6,
  LOG_ALERT_LEVEL    = 7,
  LOG_EMERG_LEVEL    = 8,
  LOG_HIGHEST_LEVEL  = 8,
  LOG_NONE_LEVEL     = 9,	/* Disables all levels */
  LOG_NLEVEL_NAMES   = LOG_NONE_LEVEL + 1,
  LOG_SENSITIVE_FLAG = 16,	/* Flags a message as potentially sensitive */
  LOG_USER_FLAG      = 32,	/* Flags as user generated */
  LOG_AUDIT_FLAG     = 64,	/* Flags as useful for audit purposes */
  LOG_LEVEL_MASK     = 0x0f,
  LOG_USE_DEFAULT_LEVEL = -1, /* Filter should use descriptor's level */
  LOG_GLOBAL_LEVEL   = -2,  /* Filter should copy current descriptor's level */
  LOG_INVALID_LEVEL  = -3,	/* An error code - no such level */
  LOG_FORCE_LEVEL    = -4	/* Emit if at all possible */
} Log_level;

typedef enum {
  LOG_DISABLED = 0,
  LOG_ENABLED  = 1
} Log_state;

/* XXX Should these be bits so combinations can be specified? */
typedef enum {
  LOG_PROGRAM   = 0,
  LOG_MODULE    = 1,
  LOG_FILENAME  = 2,
  LOG_FUNCNAME  = 3,
  LOG_ANY       = 4
} Log_source_type;

/*
 * These flags describe how an event is matched against a source.
 */
typedef enum {
  LOG_MATCH_EXACT     = (0 << 0),	/* Default is an exact string match */
  LOG_MATCH_REGEX     = (1 << 0),	/* Use a regex match */
  LOG_MATCH_ICASE     = (1 << 1),	/* Make matching case insensitive */
  LOG_MATCH_SENSITIVE = (1 << 2),	/* Allow "sensitive" events */
  LOG_MATCH_USER      = (1 << 3),	/* Allow "user" events */
  LOG_MATCH_AUDIT     = (1 << 4),	/* Allow "audit" events */
  LOG_MATCH_NORMAL    = (1 << 5)	/* Explicitly allow "normal" events */
} Log_source_flag;

typedef enum {
  LOG_OUTPUT_MINUS_MODE   = 0,
  LOG_OUTPUT_ADD_MODE     = 1,
  LOG_OUTPUT_DEFAULT_MODE = LOG_OUTPUT_MINUS_MODE
} Log_output_mode;

typedef struct Log_source_flag_map {
  char *name;
  Log_source_flag flag;
} Log_source_flag_map;

typedef struct Log_output {
  FILE *fp;
  int to_syslog;
  char *filename;
  struct Log_output *next;
} Log_output;

/*
 * The result of parsing a LOG_FILTER directive.
 */
typedef struct Log_filter_parse {
  char *str;
  Log_source_type type;
  Log_source_flag flags;
  Log_level level;
  char *source;
  char *file;
  Log_output_mode output_mode;
} Log_filter_parse;

/*
 * Each of these structures enables a logging statement for the given
 * source (program, module, file, or function) at a given logging level.
 * A level of LOG_USE_DEFAULT_LEVEL means to use the descriptor's level.
 * Note: duplicate function names and filenames are possible...
 */
typedef struct Log_filter {
  Log_source_type type;
  Log_level level;
  int id;				/* A unique identifier for this source */
  unsigned int flags;
  char *str;
  regex_t regex;
  Log_output *output;
  Log_output_mode output_mode;
  struct Log_filter *next;
} Log_filter;

/* These are set by log_set_args() to record an event. */
typedef struct Log_event {
  Log_level level;		/* The level for this event. */
  char *program;
  char *module;
  char *filename;
  char *funcname;
  int line;
  int err;				/* For log_err(), the value of errno to use. */
  char *msg;			/* For log_msg(), the message to print. */
} Log_event;

/* This describes the logging configuration. */
typedef struct Log_desc {
  Log_output *output;	/* Where logging statements are written. */
  Log_state state;		/* Enabled or disabled? */
  Log_level level;		/* The default logging level. */
  unsigned int flags;	/* Default flags */
  Log_filter *filters;	/* Possibly empty, unordered list of filters. */
  Log_event event;
  /* An optional, user-specified callback to generate a prefix string. */
  char *(*user_prefix)(struct Log_desc *, Log_level level, void *);
  void *user_data;
  char *default_prefix;	/* If non-NULL and no user_prefix, use this. */
  int (*trace_callback)(Log_event *, void *);
  void *trace_data;
} Log_desc;

extern Log_desc *log_current_logd;
extern char *log_source_type_name[];
extern Log_source_flag_map log_source_flag_map[];

static inline int
log_is_enabled(void)
{

  if (log_current_logd == NULL || log_current_logd->state == LOG_ENABLED)
	return(1);
  return(0);
}

static inline Log_level
log_get_level(Log_level level)
{
  int level_bits;

  level_bits = level & LOG_LEVEL_MASK;

  return((Log_level) level_bits);
}

static inline int
log_is_sensitive_event(Log_level level)
{

  return((level & LOG_SENSITIVE_FLAG) ? 1 : 0);
}

static inline int
log_selects_sensitive(int flags)
{

  return((flags & LOG_MATCH_SENSITIVE) ? 1 : 0);
}

static inline int
log_is_user_event(Log_level level)
{

  return((level & LOG_USER_FLAG) ? 1 : 0);
}

static inline int
log_selects_user(int flags)
{

  return((flags & LOG_MATCH_USER) ? 1 : 0);
}

static inline int
log_is_audit_event(Log_level level)
{

  return((level & LOG_AUDIT_FLAG) ? 1 : 0);
}

static inline int
log_selects_audit(int flags)
{

  return((flags & LOG_MATCH_AUDIT) ? 1 : 0);
}

static inline int
log_is_normal_event(Log_level level)
{

  return((level & (LOG_AUDIT_FLAG | LOG_USER_FLAG | LOG_SENSITIVE_FLAG))
		 ? 0 : 1);
}

static inline int
log_selects_normal(int flags)
{

  if (flags & LOG_MATCH_NORMAL)
	return(1);

  return((flags & (LOG_MATCH_SENSITIVE | LOG_MATCH_USER | LOG_MATCH_AUDIT))
		 ? 0 : 1);
}

/*
 * These are the four primitives used to log events.
 * It's unfortunate that they have to be macros, but that's the only
 * way the __FILE__ and __func__ strings can be obtained transparently
 * (without some other kind of preprocessing kludge).
 * Note that these macros take either one or two arguments, and that the
 * first must always be parenthesized.
 * Either per-file or per-program, the variable log_module_name must
 * be defined; its value becomes the "module name" for logging selection
 * purposes.  Ideally, a module represents some closely related functionality
 * in one or more files; each such file will have its own private
 * log_module_name but all of them will be assigned the same value.
 * XXX Not thread safe
 */

/* XXX Could extend to record logging calls to provide a program trace */

#define log_msg(B)	\
	do { \
		if (log_is_enabled()) { \
			log_set_args(log_module_name, __FILE__, __func__, __LINE__, \
						0, NULL); \
			log_exec B; \
		} \
	 } while(0)

#define log_vmsg(B)	\
	do { \
		if (log_is_enabled()) { \
			log_set_args(log_module_name, __FILE__, __func__, __LINE__, \
						0, NULL); \
			log_vexec B; \
		} \
	 } while(0)

#define log_err(B)	\
	do { \
		if (log_is_enabled()) { \
			log_set_args(log_module_name, __FILE__, __func__, __LINE__, \
						errno, NULL); \
			log_exec B; \
		} \
	} while(0)

#define log_basic(B, M)	\
	do { \
		if (log_is_enabled()) { \
			log_set_args(log_module_name, __FILE__, __func__, __LINE__, \
						0, M); \
			log_exec B; \
		} \
	} while(0)

#define log_would_log(LEV) \
	(log_is_enabled() \
	 ? (log_set_args(log_module_name, __FILE__, __func__, __LINE__, 0, NULL), log_test(LEV)) : 0)

#ifdef __cplusplus
extern "C" {
#endif

extern int log_add_output(Log_desc *logd, Log_output *output);
extern Log_desc *log_init(FILE *fp, int to_syslog, char *filename,
						  char *program, Log_level default_level,
						  char *default_prefix);
extern int log_end(Log_desc *logd);
extern void log_set_args(const char *module, char *filename,
						 const char *funcname, int line, int err, char *msg);
extern Log_desc *log_set_desc(Log_desc *logd, Log_state state);
extern Log_level log_set_level(Log_desc *logd, Log_level level);
extern unsigned int log_set_flags(Log_desc *logd, unsigned int flags);
extern unsigned int log_get_flags(Log_desc *logd);
extern void log_set_user_callback(Log_desc *logd,
								  char *(user_prefix)(Log_desc *,
													  Log_level level,
													  void *),
								  void *user_data);
extern void log_set_trace_callback(Log_desc *logd,
								   int (trace_callback)(Log_event *, void *),
								   void *trace_data);
extern int log_set_filter(Log_desc *logd, Log_filter_parse *lfp);
extern int log_add_filter(Log_desc *logd, char *filter_str);
extern Log_level log_lookup_level(char *level_name);
extern int log_is_valid_level(Log_level level);
extern Log_filter_parse *log_parse_filter(char *str);
extern int log_test(Log_level level);
extern char *log_event_stamp(Log_desc *logd, Log_level level, void *arg);
extern char *log_format(Log_desc *logd, Log_level level, const char *fmt);

extern int log_exec(Log_level level, const char *fmt, ...);
extern int log_vexec(Log_level level, const char *fmt, va_list ap);

#ifdef __cplusplus
}
#endif

#include "local.h"

#endif
