/** * 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; }