diff --git a/configure.ac b/configure.ac index ff4576a9..6f384d39 100644 --- a/configure.ac +++ b/configure.ac @@ -139,6 +139,16 @@ if test "$enable_test" = "yes"; then CFLAGS="$CFLAGS -DNTFS_TEST" fi +AH_TEMPLATE([NTFS_DISABLE_DEBUG_LOGGING], + [Define this if you want to compile out the debug log messages. + This will reduce the size of the binaries.]) +AC_ARG_ENABLE(debug-logging, + AS_HELP_STRING(--disable-debug-logging,Remove debug logging from the code), + if test "$enable_debug_logging" == "no"; then + AC_DEFINE(NTFS_DISABLE_DEBUG_LOGGING) + fi, +) + # Use GNU extensions if available. AC_GNU_SOURCE diff --git a/include/ntfs/Makefile.am b/include/ntfs/Makefile.am index 89608d4b..11c6ed97 100644 --- a/include/ntfs/Makefile.am +++ b/include/ntfs/Makefile.am @@ -21,6 +21,7 @@ linux_ntfsinclude_HEADERS = \ lcnalloc.h \ list.h \ logfile.h \ + logging.h \ mft.h \ mst.h \ runlist.h \ diff --git a/include/ntfs/logging.h b/include/ntfs/logging.h new file mode 100644 index 00000000..d1165ea3 --- /dev/null +++ b/include/ntfs/logging.h @@ -0,0 +1,114 @@ +/* + * logging.h - Centralised logging. Part of the Linux-NTFS project. + * + * Copyright (c) 2005 Richard Russon + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the Linux-NTFS + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _LOGGING_H_ +#define _LOGGING_H_ + +#include "config.h" + +#ifdef HAVE_STDARG_H +#include +#endif + +#include "types.h" + +struct ntfs_logging; + +/* Function prototype for the logging handlers */ +typedef int (logging_handler) (const char *handler, const char *file, int line, + int level, FILE *stream, const char *format, va_list args); + +/** + * struct ntfs_logging - Control info for the logging system + * @levels: Bitfield of logging levels + * @flags: Flags to affect the output style + * @handler: Function to perform the actual logging + */ +struct ntfs_logging { + u32 levels; + u32 flags; + logging_handler *handler; +}; + +extern struct ntfs_logging ntfs_log; + +void ntfs_logging_set_handler (logging_handler *handler); + +/* Enable/disable certain log levels */ +u32 ntfs_logging_set_levels (u32 levels); +u32 ntfs_logging_clear_levels (u32 levels); +u32 ntfs_logging_get_levels (void); + +/* Enable/disable certain log flags */ +u32 ntfs_logging_set_flags (u32 flags); +u32 ntfs_logging_clear_flags (u32 flags); +u32 ntfs_logging_get_flags (void); + +BOOL ntfs_logging_parse_option (const char *option); + +int ntfs_logging_redirect (const char *handler, const char *file, int line, + int level, FILE *stream, const char *format, ...) + __attribute__ ((format (printf, 6, 7))); + +/* Logging handlers */ +logging_handler ntfs_logging_handler_printf __attribute__ ((format (printf, 6, 0))); +logging_handler ntfs_logging_handler_colour __attribute__ ((format (printf, 6, 0))); + +/* Logging levels - Determine what gets logged */ +#define LOG_LEVEL_DEBUG (1 << 0) /* x = 42 */ +#define LOG_LEVEL_TRACE (1 << 1) /* Entering function x() */ +#define LOG_LEVEL_QUIET (1 << 2) /* Quietable output */ +#define LOG_LEVEL_INFO (1 << 3) /* Volume needs defragmenting */ +#define LOG_LEVEL_VERBOSE (1 << 4) /* Forced to continue */ +#define LOG_LEVEL_PROGRESS (1 << 5) /* 54% complete */ +#define LOG_LEVEL_WARNING (1 << 6) /* You should backup before starting */ +#define LOG_LEVEL_ERROR (1 << 7) /* Operation failed, no damage done */ +#define LOG_LEVEL_PERROR (1 << 8) /* Message : standard error description */ +#define LOG_LEVEL_CRITICAL (1 << 9) /* Operation failed,damage may have occurred */ + +/* Logging style flags - Manage the style of the output */ +#define LOG_FLAG_PREFIX (1 << 0) /* Prefix messages with "ERROR: ", etc */ +#define LOG_FLAG_FILENAME (1 << 1) /* Show the file origin of the message */ +#define LOG_FLAG_LINE (1 << 2) /* Show the line number of the message */ +#define LOG_FLAG_FUNCTION (1 << 3) /* Show the function name containing the message */ + +/* Macros to simplify logging. One for each level defined above. + * Note, if DEBUG isn't defined, then log_debug has no effect. + */ +#define log_crit(FORMAT, ARGS...) ntfs_logging_redirect (__FUNCTION__,__FILE__,__LINE__,LOG_LEVEL_CRITICAL,NULL,FORMAT,##ARGS) +#define log_error(FORMAT, ARGS...) ntfs_logging_redirect (__FUNCTION__,__FILE__,__LINE__,LOG_LEVEL_ERROR,NULL,FORMAT,##ARGS) +#define log_info(FORMAT, ARGS...) ntfs_logging_redirect (__FUNCTION__,__FILE__,__LINE__,LOG_LEVEL_INFO,NULL,FORMAT,##ARGS) +#define log_perror(FORMAT, ARGS...) ntfs_logging_redirect (__FUNCTION__,__FILE__,__LINE__,LOG_LEVEL_PERROR,NULL,FORMAT,##ARGS) +#define log_progress(FORMAT, ARGS...) ntfs_logging_redirect (__FUNCTION__,__FILE__,__LINE__,LOG_LEVEL_PROGRESS,NULL,FORMAT,##ARGS) +#define log_quiet(FORMAT, ARGS...) ntfs_logging_redirect (__FUNCTION__,__FILE__,__LINE__,LOG_LEVEL_QUIET,NULL,FORMAT,##ARGS) +#define log_verbose(FORMAT, ARGS...) ntfs_logging_redirect (__FUNCTION__,__FILE__,__LINE__,LOG_LEVEL_VERBOSE,NULL,FORMAT,##ARGS) +#define log_warn(FORMAT, ARGS...) ntfs_logging_redirect (__FUNCTION__,__FILE__,__LINE__,LOG_LEVEL_WARNING,NULL,FORMAT,##ARGS) + +#ifdef NTFS_DISABLE_DEBUG_LOGGING +#define log_debug(FORMAT, ARGS...)do {} while (0) +#define log_trace(FORMAT, ARGS...)do {} while (0) +#else +#define log_debug(FORMAT, ARGS...) ntfs_logging_redirect (__FUNCTION__,__FILE__,__LINE__,LOG_LEVEL_DEBUG,NULL,FORMAT,##ARGS) +#define log_trace(FORMAT, ARGS...) ntfs_logging_redirect (__FUNCTION__,__FILE__,__LINE__,LOG_LEVEL_TRACE,NULL,FORMAT,##ARGS) +#endif /* NTFS_DISABLE_DEBUG_LOGGING */ + +#endif /* _LOGGING_H_ */ + diff --git a/libntfs/Makefile.am b/libntfs/Makefile.am index e199c9d4..4828c850 100644 --- a/libntfs/Makefile.am +++ b/libntfs/Makefile.am @@ -51,6 +51,7 @@ libntfs_la_SOURCES = \ inode.c \ lcnalloc.c \ logfile.c \ + logging.c \ mft.c \ mst.c \ runlist.c \ diff --git a/libntfs/logging.c b/libntfs/logging.c new file mode 100644 index 00000000..6aba1027 --- /dev/null +++ b/libntfs/logging.c @@ -0,0 +1,449 @@ +/** + * logging.c - Centralised logging. Part of the Linux-NTFS project. + * + * Copyright (c) 2005 Richard Russon + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the Linux-NTFS + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "config.h" + +#ifdef HAVE_STDIO_H +#include +#endif +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_STDARG_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif + +#include "logging.h" + +#ifndef PATH_SEP +#define PATH_SEP '/' +#endif + +/** + * struct ntfs_logging + * This global struct controls all the logging within the library and tools. + */ +struct ntfs_logging ntfs_log = +{ +#ifdef DEBUG + LOG_LEVEL_DEBUG | LOG_LEVEL_TRACE | +#endif + LOG_LEVEL_INFO | LOG_LEVEL_WARNING | LOG_LEVEL_ERROR | LOG_LEVEL_PERROR | LOG_LEVEL_CRITICAL, + LOG_FLAG_PREFIX, + ntfs_logging_handler_printf +}; + +/** + * ntfs_logging_get_levels - Get a list of the current logging levels + * + * Find out which logging levels are enabled. + * + * Returns: Log levels in a 32-bit field + */ +u32 ntfs_logging_get_levels (void) +{ + return ntfs_log.levels; +} + +/** + * ntfs_logging_set_levels - Enable extra logging levels + * @levels: 32-bit field of log levels to set + * + * Enable one or more logging levels. + * The logging levels are named: LOG_LEVEL_*. + * + * Returns: Log levels that were enabled before the call + */ +u32 ntfs_logging_set_levels (u32 levels) +{ + u32 old; + old = ntfs_log.levels; + ntfs_log.levels |= levels; + return old; +} + +/** + * ntfs_logging_clear_levels - Disable some logging levels + * @levels: 32-bit field of log levels to clear + * + * Disable one or more logging levels. + * The logging levels are named: LOG_LEVEL_*. + * + * Returns: Log levels that were enabled before the call + */ +u32 ntfs_logging_clear_levels (u32 levels) +{ + u32 old; + old = ntfs_log.levels; + ntfs_log.levels &= (~levels); + return old; +} + + +/** + * ntfs_logging_get_flags - Get a list of logging style flags + * + * Find out which logging flags are enabled. + * + * Returns: Logging flags in a 32-bit field + */ +u32 ntfs_logging_get_flags (void) +{ + return ntfs_log.flags; +} + +/** + * ntfs_logging_set_flags - Enable extra logging style flags + * @flags: 32-bit field of logging flags to set + * + * Enable one or more logging flags. + * The log flags are named: LOG_LEVEL_*. + * + * Returns: Logging flags that were enabled before the call + */ +u32 ntfs_logging_set_flags (u32 flags) +{ + u32 old; + old = ntfs_log.flags; + ntfs_log.flags |= flags; + return old; +} + +/** + * ntfs_logging_clear_flags - Disable some logging styles + * @flags: 32-bit field of logging flags to clear + * + * Disable one or more logging flags. + * The log flags are named: LOG_LEVEL_*. + * + * Returns: Logging flags that were enabled before the call + */ +u32 ntfs_logging_clear_flags (u32 flags) +{ + u32 old; + old = ntfs_log.flags; + ntfs_log.flags &= (~flags); + return old; +} + + +/** + * ntfs_logging_get_stream - Default output streams for logging levels + * @level: Log level + * + * By default, urgent messages are sent to "stderr". + * Other messages are sent to "stdout". + * + * Returns: "string" Prefix to be used + */ +static FILE * ntfs_logging_get_stream (int level) +{ + FILE *stream; + + switch (level) { + case LOG_LEVEL_INFO: + case LOG_LEVEL_QUIET: + case LOG_LEVEL_PROGRESS: + stream = stdout; + break; + + case LOG_LEVEL_DEBUG: + case LOG_LEVEL_TRACE: + case LOG_LEVEL_WARNING: + case LOG_LEVEL_ERROR: + case LOG_LEVEL_CRITICAL: + case LOG_LEVEL_PERROR: + default: + stream = stderr; + break; + } + + return stream; +} + +/** + * ntfs_logging_get_prefix - Default prefixes for logging levels + * @level: Log level to be prefixed + * + * Prefixing the logging output can make it easier to parse. + * + * Returns: "string" Prefix to be used + */ +static const char * ntfs_logging_get_prefix (int level) +{ + const char *prefix; + + switch (level) { + case LOG_LEVEL_DEBUG: + prefix = "DEBUG: "; + break; + case LOG_LEVEL_TRACE: + prefix = "TRACE: "; + break; + case LOG_LEVEL_QUIET: + prefix = "QUIET: "; + break; + case LOG_LEVEL_INFO: + prefix = "INFO: "; + break; + case LOG_LEVEL_VERBOSE: + prefix = "VERBOSE: "; + break; + case LOG_LEVEL_PROGRESS: + prefix = "PROGRESS: "; + break; + case LOG_LEVEL_WARNING: + prefix = "WARNING: "; + break; + case LOG_LEVEL_ERROR: + prefix = "ERROR: "; + break; + case LOG_LEVEL_PERROR: + prefix = "ERROR: "; + break; + case LOG_LEVEL_CRITICAL: + prefix = "CRITICAL: "; + break; + default: + prefix = ""; + break; + } + + return prefix; +} + + +/** + * ntfs_logging_set_handler - Provide an alternate logging handler + * @handler: function to perform the logging + * + * This alternate handler will be called for all future logging requests. + * If no @handler is specified, logging will revert to the default handler. + * + * Returns: void + */ +void ntfs_logging_set_handler (logging_handler *handler) +{ + if (handler) + ntfs_log.handler = handler; + else + ntfs_log.handler = ntfs_logging_handler_printf; +} + +/** + * ntfs_logging_redirect - Pass on the request to the real handler + * @function: Function in which the log line occurred + * @file: File in which the log line occurred + * @line: Line number on which the log line occurred + * @level: Level at which the line is logged + * @stream: FILE stream to output to (may be NULL) + * @format: printf-style formatting string + * @...: Arguments to be formatted + * + * This is just a redirector function. The arguments are simply passed to the + * main logging handler (as defined in the global logging struct @ntfs_log). + * + * Note: If @stream is NULL, the output stream will be determined by the + * function: ntfs_logging_get_stream + * + * Returns: -1 Error occurred + * 0 Message wasn't logged + * num Number of output characters + */ +int ntfs_logging_redirect (const char *function, const char *file, + int line, int level, FILE *stream, const char *format, ...) +{ + int olderr = errno; + int ret; + va_list args; + + if (!(ntfs_log.levels & level)) /* Don't log this message */ + return 0; + + va_start (args, format); + errno = olderr; + ret = ntfs_log.handler (function, file, line, level, stream, format, args); + va_end (args); + + errno = olderr; + return ret; +} + +/** + * ntfs_logging_handler_printf - Basic logging handler + * @function: Function in which the log line occurred + * @file: File in which the log line occurred + * @line: Line number on which the log line occurred + * @level: Level at which the line is logged + * @stream: FILE stream to output to (may be NULL) + * @format: printf-style formatting string + * @args: Arguments to be formatted + * + * A simple logging handler. This is where the log line is finally displayed. + * + * Note: If @stream is NULL, the output stream will be determined by the + * function: ntfs_logging_get_stream + * + * Returns: -1 Error occurred + * 0 Message wasn't logged + * num Number of output characters + */ +int ntfs_logging_handler_printf (const char *function, const char *file, + int line, int level, FILE *stream, const char *format, va_list args) +{ + int ret = 0; + int olderr = errno; + + if (!stream) + stream = ntfs_logging_get_stream (level); + + if (strchr (file, PATH_SEP)) /* Abbreviate the filename */ + file = strrchr (file, PATH_SEP) + 1; + + if (ntfs_log.flags & LOG_FLAG_PREFIX) /* Prefix the output */ + ret += fprintf (stream, "%s", ntfs_logging_get_prefix (level)); + + if (ntfs_log.flags & LOG_FLAG_FILENAME) /* Source filename */ + ret += fprintf (stream, "%s ", file); + + if (ntfs_log.flags & LOG_FLAG_LINE) /* Source line number */ + ret += fprintf (stream, "(%d) ", line); + + if (ntfs_log.flags & LOG_FLAG_FUNCTION) /* Source function */ + ret += fprintf (stream, ": %s : ", function); + + if (level & LOG_LEVEL_PERROR) { + errno = olderr; + ret += fprintf (stream, "<%s> : ", strerror (olderr)); + } + + ret += vfprintf (stream, format, args); + + errno = olderr; + return ret; +} + +/** + * ntfs_logging_handler_colour - Colour-highlighting logging handler + * @function: Function in which the log line occurred + * @file: File in which the log line occurred + * @line: Line number on which the log line occurred + * @level: Level at which the line is logged + * @stream: FILE stream to output to (may be NULL) + * @format: printf-style formatting string + * @args: Arguments to be formatted + * + * This is a simple logging filter that prefixes/suffixes some logs. + * Warnings: yellow + * Errors: red + * Critical errors: red (inverse video) + * + * Note: This function calls ntfs_logging_handler_printf to do the main work. + * + * Note: If @stream is NULL, the output stream will be determined by the + * function: ntfs_logging_get_stream + * + * Returns: -1 Error occurred + * 0 Message wasn't logged + * num Number of output characters + */ +int ntfs_logging_handler_colour (const char *function, const char *file, + int line, int level, FILE *stream, const char *format, va_list args) +{ + int ret = 0; + int olderr = errno; + const char *prefix = NULL; + const char *suffix = NULL; + const char *end = "\e[0m"; + + if (!stream) + stream = ntfs_logging_get_stream (level); + + switch (level) { + case LOG_LEVEL_WARNING: + prefix = "\e[01;33m"; /* Yellow */ + suffix = end; + break; + case LOG_LEVEL_ERROR: + case LOG_LEVEL_PERROR: + prefix = "\e[01;31m"; /* Red */ + suffix = end; + break; + case LOG_LEVEL_CRITICAL: + prefix = "\e[01;07;31m"; /* Red, inverse */ + suffix = end; + break; + } + + if (prefix) + ret += fprintf (stream, prefix); + + errno = olderr; + ret += ntfs_logging_handler_printf (function, file, line, level, stream, format, args); + + if (suffix) + ret += fprintf (stream, suffix); + + errno = olderr; + return ret; +} + + +/** + * ntfs_logging_parse_option - Act upon command line options + * @option: Option flag + * + * Delegate some of the work of parsing the command line. All the options begin + * with "--log-". Options cause log levels to be enabled in @ntfs_log (the + * global logging structure). + * + * Note: The "colour" option changes the logging handler. + * + * Returns: TRUE Option understood + * FALSE Invalid log option + */ +BOOL ntfs_logging_parse_option (const char *option) +{ + if (strcmp (option, "--log-debug") == 0) { + ntfs_logging_set_levels (LOG_LEVEL_DEBUG); + return TRUE; + } else if (strcmp (option, "--log-verbose") == 0) { + ntfs_logging_set_levels (LOG_LEVEL_VERBOSE); + return TRUE; + } else if (strcmp (option, "--log-quiet") == 0) { + ntfs_logging_set_levels (LOG_LEVEL_QUIET); + return TRUE; + } else if (strcmp (option, "--log-trace") == 0) { + ntfs_logging_set_levels (LOG_LEVEL_TRACE); + return TRUE; + } else if ((strcmp (option, "--log-colour") == 0) || + (strcmp (option, "--log-color") == 0)) { + ntfs_logging_set_handler (ntfs_logging_handler_colour); + return TRUE; + } + + log_error ("Unknown logging option '%s'\n", option); + return FALSE; +} +