1 /**
   2  * logging.c - Centralised logging.  Part of the Linux-NTFS project.
   3  *
   4  * Copyright (c) 2005 Richard Russon
   5  *
   6  * This program/include file is free software; you can redistribute it and/or
   7  * modify it under the terms of the GNU General Public License as published
   8  * by the Free Software Foundation; either version 2 of the License, or
   9  * (at your option) any later version.
  10  *
  11  * This program/include file is distributed in the hope that it will be
  12  * useful, but WITHOUT ANY WARRANTY; without even the implied warranty
  13  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14  * GNU General Public License for more details.
  15  *
  16  * You should have received a copy of the GNU General Public License
  17  * along with this program (in the main directory of the Linux-NTFS
  18  * distribution in the file COPYING); if not, write to the Free Software
  19  * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  20  */
  21 
  22 #ifdef HAVE_CONFIG_H
  23 #include "config.h"
  24 #endif
  25 
  26 #ifdef HAVE_STDIO_H
  27 #include <stdio.h>
  28 #endif
  29 #ifdef HAVE_ERRNO_H
  30 #include <errno.h>
  31 #endif
  32 #ifdef HAVE_STDARG_H
  33 #include <stdarg.h>
  34 #endif
  35 #ifdef HAVE_STRING_H
  36 #include <string.h>
  37 #endif
  38 #ifdef HAVE_STDLIB_H
  39 #include <stdlib.h>
  40 #endif
  41 #ifdef HAVE_SYSLOG_H
  42 #include <syslog.h>
  43 #endif
  44 
  45 #include "compat.h"
  46 #include "logging.h"
  47 
  48 #ifndef PATH_SEP
  49 #define PATH_SEP '/'
  50 #endif
  51 
  52 /* Colour prefixes and a suffix */
  53 #ifdef __sun
  54 static const char *col_green  = "\033[32m";
  55 static const char *col_cyan   = "\033[36m";
  56 static const char *col_yellow = "\033[01;33m";
  57 static const char *col_red    = "\033[01;31m";
  58 static const char *col_redinv = "\033[01;07;31m";
  59 static const char *col_end    = "\033[0m";
  60 #else /* ! __sun */
  61 static const char *col_green  = "\e[32m";
  62 static const char *col_cyan   = "\e[36m";
  63 static const char *col_yellow = "\e[01;33m";
  64 static const char *col_red    = "\e[01;31m";
  65 static const char *col_redinv = "\e[01;07;31m";
  66 static const char *col_end    = "\e[0m";
  67 #endif /* __sun */
  68 
  69 /**
  70  * struct ntfs_logging - Control info for the logging system
  71  * @levels:     Bitfield of logging levels
  72  * @flags:      Flags which affect the output style
  73  * @handler:    Function to perform the actual logging
  74  */
  75 struct ntfs_logging {
  76         u32 levels;
  77         u32 flags;
  78         ntfs_log_handler *handler;
  79 };
  80 
  81 /**
  82  * ntfs_log - This struct controls all the logging in the library and tools.
  83  */
  84 static struct ntfs_logging ntfs_log = {
  85         .levels = NTFS_LOG_LEVEL_INFO | NTFS_LOG_LEVEL_QUIET |
  86                         NTFS_LOG_LEVEL_WARNING | NTFS_LOG_LEVEL_ERROR |
  87                         NTFS_LOG_LEVEL_PERROR | NTFS_LOG_LEVEL_CRITICAL |
  88                         NTFS_LOG_LEVEL_PROGRESS |
  89                         0,
  90         .flags = NTFS_LOG_FLAG_ONLYNAME,
  91         .handler = ntfs_log_handler_null,
  92 };
  93 
  94 
  95 /**
  96  * ntfs_log_get_levels - Get a list of the current logging levels
  97  *
  98  * Find out which logging levels are enabled.
  99  *
 100  * Returns:  Log levels in a 32-bit field
 101  */
 102 u32 ntfs_log_get_levels(void)
 103 {
 104         return ntfs_log.levels;
 105 }
 106 
 107 /**
 108  * ntfs_log_set_levels - Enable extra logging levels
 109  * @levels:     32-bit field of log levels to set
 110  *
 111  * Enable one or more logging levels.
 112  * The logging levels are named: NTFS_LOG_LEVEL_*.
 113  *
 114  * Returns:  Log levels that were enabled before the call
 115  */
 116 u32 ntfs_log_set_levels(u32 levels)
 117 {
 118         u32 old;
 119         old = ntfs_log.levels;
 120         ntfs_log.levels |= levels;
 121         return old;
 122 }
 123 
 124 /**
 125  * ntfs_log_clear_levels - Disable some logging levels
 126  * @levels:     32-bit field of log levels to clear
 127  *
 128  * Disable one or more logging levels.
 129  * The logging levels are named: NTFS_LOG_LEVEL_*.
 130  *
 131  * Returns:  Log levels that were enabled before the call
 132  */
 133 u32 ntfs_log_clear_levels(u32 levels)
 134 {
 135         u32 old;
 136         old = ntfs_log.levels;
 137         ntfs_log.levels &= (~levels);
 138         return old;
 139 }
 140 
 141 
 142 /**
 143  * ntfs_log_get_flags - Get a list of logging style flags
 144  *
 145  * Find out which logging flags are enabled.
 146  *
 147  * Returns:  Logging flags in a 32-bit field
 148  */
 149 u32 ntfs_log_get_flags(void)
 150 {
 151         return ntfs_log.flags;
 152 }
 153 
 154 /**
 155  * ntfs_log_set_flags - Enable extra logging style flags
 156  * @flags:      32-bit field of logging flags to set
 157  *
 158  * Enable one or more logging flags.
 159  * The log flags are named: NTFS_LOG_LEVEL_*.
 160  *
 161  * Returns:  Logging flags that were enabled before the call
 162  */
 163 u32 ntfs_log_set_flags(u32 flags)
 164 {
 165         u32 old;
 166         old = ntfs_log.flags;
 167         ntfs_log.flags |= flags;
 168         return old;
 169 }
 170 
 171 /**
 172  * ntfs_log_clear_flags - Disable some logging styles
 173  * @flags:      32-bit field of logging flags to clear
 174  *
 175  * Disable one or more logging flags.
 176  * The log flags are named: NTFS_LOG_LEVEL_*.
 177  *
 178  * Returns:  Logging flags that were enabled before the call
 179  */
 180 u32 ntfs_log_clear_flags(u32 flags)
 181 {
 182         u32 old;
 183         old = ntfs_log.flags;
 184         ntfs_log.flags &= (~flags);
 185         return old;
 186 }
 187 
 188 
 189 /**
 190  * ntfs_log_get_stream - Default output streams for logging levels
 191  * @level:      Log level
 192  *
 193  * By default, urgent messages are sent to "stderr".
 194  * Other messages are sent to "stdout".
 195  *
 196  * Returns:  "string"  Prefix to be used
 197  */
 198 static FILE * ntfs_log_get_stream(u32 level)
 199 {
 200         FILE *stream;
 201 
 202         switch (level) {
 203                 case NTFS_LOG_LEVEL_INFO:
 204                 case NTFS_LOG_LEVEL_QUIET:
 205                 case NTFS_LOG_LEVEL_PROGRESS:
 206                 case NTFS_LOG_LEVEL_VERBOSE:
 207                         stream = stdout;
 208                         break;
 209 
 210                 case NTFS_LOG_LEVEL_DEBUG:
 211                 case NTFS_LOG_LEVEL_TRACE:
 212                 case NTFS_LOG_LEVEL_WARNING:
 213                 case NTFS_LOG_LEVEL_ERROR:
 214                 case NTFS_LOG_LEVEL_CRITICAL:
 215                 case NTFS_LOG_LEVEL_PERROR:
 216                 default:
 217                         stream = stderr;
 218                         break;
 219         }
 220 
 221         return stream;
 222 }
 223 
 224 /**
 225  * ntfs_log_get_prefix - Default prefixes for logging levels
 226  * @level:      Log level to be prefixed
 227  *
 228  * Prefixing the logging output can make it easier to parse.
 229  *
 230  * Returns:  "string"  Prefix to be used
 231  */
 232 static const char * ntfs_log_get_prefix(u32 level)
 233 {
 234         const char *prefix;
 235 
 236         switch (level) {
 237                 case NTFS_LOG_LEVEL_DEBUG:
 238                         prefix = "DEBUG: ";
 239                         break;
 240                 case NTFS_LOG_LEVEL_TRACE:
 241                         prefix = "TRACE: ";
 242                         break;
 243                 case NTFS_LOG_LEVEL_QUIET:
 244                         prefix = "QUIET: ";
 245                         break;
 246                 case NTFS_LOG_LEVEL_INFO:
 247                         prefix = "INFO: ";
 248                         break;
 249                 case NTFS_LOG_LEVEL_VERBOSE:
 250                         prefix = "VERBOSE: ";
 251                         break;
 252                 case NTFS_LOG_LEVEL_PROGRESS:
 253                         prefix = "PROGRESS: ";
 254                         break;
 255                 case NTFS_LOG_LEVEL_WARNING:
 256                         prefix = "WARNING: ";
 257                         break;
 258                 case NTFS_LOG_LEVEL_ERROR:
 259                         prefix = "ERROR: ";
 260                         break;
 261                 case NTFS_LOG_LEVEL_PERROR:
 262                         prefix = "ERROR: ";
 263                         break;
 264                 case NTFS_LOG_LEVEL_CRITICAL:
 265                         prefix = "CRITICAL: ";
 266                         break;
 267                 default:
 268                         prefix = "";
 269                         break;
 270         }
 271 
 272         return prefix;
 273 }
 274 
 275 
 276 /**
 277  * ntfs_log_set_handler - Provide an alternate logging handler
 278  * @handler:    function to perform the logging
 279  *
 280  * This alternate handler will be called for all future logging requests.
 281  * If no @handler is specified, logging will revert to the default handler.
 282  */
 283 void ntfs_log_set_handler(ntfs_log_handler *handler)
 284 {
 285         if (handler) {
 286                 ntfs_log.handler = handler;
 287 #ifdef HAVE_SYSLOG_H
 288                 if (handler == ntfs_log_handler_syslog)
 289                         openlog("libntfs", LOG_PID, LOG_USER);
 290 #endif
 291         } else
 292                 ntfs_log.handler = ntfs_log_handler_null;
 293 }
 294 
 295 /**
 296  * ntfs_log_redirect - Pass on the request to the real handler
 297  * @function:   Function in which the log line occurred
 298  * @file:       File in which the log line occurred
 299  * @line:       Line number on which the log line occurred
 300  * @level:      Level at which the line is logged
 301  * @data:       User specified data, possibly specific to a handler
 302  * @format:     printf-style formatting string
 303  * @...:        Arguments to be formatted
 304  *
 305  * This is just a redirector function.  The arguments are simply passed to the
 306  * main logging handler (as defined in the global logging struct @ntfs_log).
 307  *
 308  * Returns:  -1  Error occurred
 309  *            0  Message wasn't logged
 310  *          num  Number of output characters
 311  */
 312 int ntfs_log_redirect(const char *function, const char *file,
 313         int line, u32 level, void *data, const char *format, ...)
 314 {
 315         int olderr = errno;
 316         int ret;
 317         va_list args;
 318 
 319         if (!(ntfs_log.levels & level))             /* Don't log this message */
 320                 return 0;
 321 
 322         va_start(args, format);
 323         errno = olderr;
 324         ret = ntfs_log.handler(function, file, line, level, data, format, args);
 325         va_end(args);
 326 
 327         errno = olderr;
 328         return ret;
 329 }
 330 
 331 
 332 #ifdef HAVE_SYSLOG_H
 333 /**
 334  * ntfs_log_handler_syslog - syslog logging handler
 335  * @function:   Function in which the log line occurred
 336  * @file:       File in which the log line occurred
 337  * @line:       Line number on which the log line occurred
 338  * @level:      Level at which the line is logged
 339  * @data:       User specified data, possibly specific to a handler
 340  * @format:     printf-style formatting string
 341  * @args:       Arguments to be formatted
 342  *
 343  * A syslog logging handler. Ignores colors and truncates output after 512
 344  * bytes.
 345  *
 346  * Returns:  -1  Error occurred
 347  *            0  Message wasn't logged
 348  *          num  Number of output characters
 349  */
 350 int ntfs_log_handler_syslog(const char *function, const char *file, int line,
 351                 u32 level, void *data __attribute__((unused)),
 352                 const char *format, va_list args)
 353 {
 354         char buffer[512];
 355         int ret = 0, olderr = errno;
 356 
 357         if ((ntfs_log.flags & NTFS_LOG_FLAG_ONLYNAME) &&
 358             (strchr(file, PATH_SEP)))           /* Abbreviate the filename */
 359                 file = strrchr(file, PATH_SEP) + 1;
 360 
 361         /* Prefix the output */
 362         if (ret < sizeof(buffer) && ntfs_log.flags & NTFS_LOG_FLAG_PREFIX)
 363                 ret += snprintf(buffer + ret, sizeof(buffer) - ret, "%s",
 364                                 ntfs_log_get_prefix(level));
 365 
 366         /* Source filename */
 367         if (ret < sizeof(buffer) && ntfs_log.flags & NTFS_LOG_FLAG_FILENAME)
 368                 ret += snprintf(buffer + ret, sizeof(buffer) - ret, "%s ",
 369                                 file);
 370 
 371         /* Source line number */
 372         if (ret < sizeof(buffer) && ntfs_log.flags & NTFS_LOG_FLAG_LINE)
 373                 ret += snprintf(buffer + ret, sizeof(buffer) - ret, "(%d) ",
 374                                 line);
 375 
 376         /* Source function */
 377         if (ret < sizeof(buffer) && ((ntfs_log.flags & NTFS_LOG_FLAG_FUNCTION)
 378                         || (level & NTFS_LOG_LEVEL_TRACE)))
 379                 ret += snprintf(buffer + ret, sizeof(buffer) - ret, "%s(): ",
 380                                 function);
 381 
 382         /* Message itself */
 383         if (ret < sizeof(buffer))
 384                 ret += vsnprintf(buffer + ret, sizeof(buffer) - ret, format,
 385                                 args);
 386 
 387         /* Append errno */
 388         if (ret < sizeof(buffer) && level & NTFS_LOG_LEVEL_PERROR)
 389                 ret += snprintf(buffer + ret, sizeof(buffer) - ret, ": %s.\n",
 390                                 strerror(olderr));
 391 
 392         syslog(LOG_NOTICE, "%s", buffer);
 393 
 394         errno = olderr;
 395         return ret;
 396 }
 397 #endif
 398 
 399 /**
 400  * ntfs_log_handler_fprintf - Basic logging handler
 401  * @function:   Function in which the log line occurred
 402  * @file:       File in which the log line occurred
 403  * @line:       Line number on which the log line occurred
 404  * @level:      Level at which the line is logged
 405  * @data:       User specified data, possibly specific to a handler
 406  * @format:     printf-style formatting string
 407  * @args:       Arguments to be formatted
 408  *
 409  * A simple logging handler.  This is where the log line is finally displayed.
 410  * It is more likely that you will want to set the handler to either
 411  * ntfs_log_handler_outerr or ntfs_log_handler_stderr.
 412  *
 413  * Note: For this handler, @data is a pointer to a FILE output stream.
 414  *       If @data is NULL, nothing will be displayed.
 415  *
 416  * Returns:  -1  Error occurred
 417  *            0  Message wasn't logged
 418  *          num  Number of output characters
 419  */
 420 int ntfs_log_handler_fprintf(const char *function, const char *file,
 421         int line, u32 level, void *data, const char *format, va_list args)
 422 {
 423         int ret = 0;
 424         int olderr = errno;
 425         FILE *stream;
 426         const char *col_prefix = NULL;
 427         const char *col_suffix = NULL;
 428 
 429         if (!data)              /* Interpret data as a FILE stream. */
 430                 return 0;       /* If it's NULL, we can't do anything. */
 431         stream = (FILE*)data;
 432 
 433         if (ntfs_log.flags & NTFS_LOG_FLAG_COLOUR) {
 434                 /* Pick a colour determined by the log level */
 435                 switch (level) {
 436                         case NTFS_LOG_LEVEL_DEBUG:
 437                                 col_prefix = col_green;
 438                                 col_suffix = col_end;
 439                                 break;
 440                         case NTFS_LOG_LEVEL_TRACE:
 441                                 col_prefix = col_cyan;
 442                                 col_suffix = col_end;
 443                                 break;
 444                         case NTFS_LOG_LEVEL_WARNING:
 445                                 col_prefix = col_yellow;
 446                                 col_suffix = col_end;
 447                                 break;
 448                         case NTFS_LOG_LEVEL_ERROR:
 449                         case NTFS_LOG_LEVEL_PERROR:
 450                                 col_prefix = col_red;
 451                                 col_suffix = col_end;
 452                                 break;
 453                         case NTFS_LOG_LEVEL_CRITICAL:
 454                                 col_prefix = col_redinv;
 455                                 col_suffix = col_end;
 456                                 break;
 457                 }
 458         }
 459 
 460         if (col_prefix)
 461                 ret += fprintf(stream, col_prefix);
 462 
 463         if ((ntfs_log.flags & NTFS_LOG_FLAG_ONLYNAME) &&
 464             (strchr(file, PATH_SEP)))           /* Abbreviate the filename */
 465                 file = strrchr(file, PATH_SEP) + 1;
 466 
 467         if (ntfs_log.flags & NTFS_LOG_FLAG_PREFIX)  /* Prefix the output */
 468                 ret += fprintf(stream, "%s", ntfs_log_get_prefix(level));
 469 
 470         if (ntfs_log.flags & NTFS_LOG_FLAG_FILENAME)        /* Source filename */
 471                 ret += fprintf(stream, "%s ", file);
 472 
 473         if (ntfs_log.flags & NTFS_LOG_FLAG_LINE)    /* Source line number */
 474                 ret += fprintf(stream, "(%d) ", line);
 475 
 476         if ((ntfs_log.flags & NTFS_LOG_FLAG_FUNCTION) || /* Source function */
 477             (level & NTFS_LOG_LEVEL_TRACE))
 478                 ret += fprintf(stream, "%s(): ", function);
 479 
 480         ret += vfprintf(stream, format, args);
 481 
 482         if (level & NTFS_LOG_LEVEL_PERROR)
 483                 ret += fprintf(stream, ": %s.\n", strerror(olderr));
 484 
 485         if (col_suffix)
 486                 ret += fprintf(stream, col_suffix);
 487 
 488 
 489         fflush(stream);
 490         errno = olderr;
 491         return ret;
 492 }
 493 
 494 /**
 495  * ntfs_log_handler_null - Null logging handler (no output)
 496  * @function:   Function in which the log line occurred
 497  * @file:       File in which the log line occurred
 498  * @line:       Line number on which the log line occurred
 499  * @level:      Level at which the line is logged
 500  * @data:       User specified data, possibly specific to a handler
 501  * @format:     printf-style formatting string
 502  * @args:       Arguments to be formatted
 503  *
 504  * This handler produces no output.  It provides a way to temporarily disable
 505  * logging, without having to change the levels and flags.
 506  *
 507  * Returns:  0  Message wasn't logged
 508  */
 509 int ntfs_log_handler_null(const char *function __attribute__((unused)), const char *file __attribute__((unused)),
 510         int line __attribute__((unused)), u32 level __attribute__((unused)), void *data __attribute__((unused)),
 511         const char *format __attribute__((unused)), va_list args __attribute__((unused)))
 512 {
 513         return 0;
 514 }
 515 
 516 /**
 517  * ntfs_log_handler_stdout - All logs go to stdout
 518  * @function:   Function in which the log line occurred
 519  * @file:       File in which the log line occurred
 520  * @line:       Line number on which the log line occurred
 521  * @level:      Level at which the line is logged
 522  * @data:       User specified data, possibly specific to a handler
 523  * @format:     printf-style formatting string
 524  * @args:       Arguments to be formatted
 525  *
 526  * Display a log message to stdout.
 527  *
 528  * Note: For this handler, @data is a pointer to a FILE output stream.
 529  *       If @data is NULL, then stdout will be used.
 530  *
 531  * Note: This function calls ntfs_log_handler_fprintf to do the main work.
 532  *
 533  * Returns:  -1  Error occurred
 534  *            0  Message wasn't logged
 535  *          num  Number of output characters
 536  */
 537 int ntfs_log_handler_stdout(const char *function, const char *file,
 538         int line, u32 level, void *data, const char *format, va_list args)
 539 {
 540         if (!data)
 541                 data = stdout;
 542 
 543         return ntfs_log_handler_fprintf(function, file, line, level, data, format, args);
 544 }
 545 
 546 /**
 547  * ntfs_log_handler_outerr - Logs go to stdout/stderr depending on level
 548  * @function:   Function in which the log line occurred
 549  * @file:       File in which the log line occurred
 550  * @line:       Line number on which the log line occurred
 551  * @level:      Level at which the line is logged
 552  * @data:       User specified data, possibly specific to a handler
 553  * @format:     printf-style formatting string
 554  * @args:       Arguments to be formatted
 555  *
 556  * Display a log message.  The output stream will be determined by the log
 557  * level.
 558  *
 559  * Note: For this handler, @data is a pointer to a FILE output stream.
 560  *       If @data is NULL, the function ntfs_log_get_stream will be called
 561  *
 562  * Note: This function calls ntfs_log_handler_fprintf to do the main work.
 563  *
 564  * Returns:  -1  Error occurred
 565  *            0  Message wasn't logged
 566  *          num  Number of output characters
 567  */
 568 int ntfs_log_handler_outerr(const char *function, const char *file,
 569         int line, u32 level, void *data, const char *format, va_list args)
 570 {
 571         if (!data)
 572                 data = ntfs_log_get_stream(level);
 573 
 574         return ntfs_log_handler_fprintf(function, file, line, level, data, format, args);
 575 }
 576 
 577 /**
 578  * ntfs_log_handler_stderr - All logs go to stderr
 579  * @function:   Function in which the log line occurred
 580  * @file:       File in which the log line occurred
 581  * @line:       Line number on which the log line occurred
 582  * @level:      Level at which the line is logged
 583  * @data:       User specified data, possibly specific to a handler
 584  * @format:     printf-style formatting string
 585  * @args:       Arguments to be formatted
 586  *
 587  * Display a log message to stderr.
 588  *
 589  * Note: For this handler, @data is a pointer to a FILE output stream.
 590  *       If @data is NULL, then stdout will be used.
 591  *
 592  * Note: This function calls ntfs_log_handler_fprintf to do the main work.
 593  *
 594  * Returns:  -1  Error occurred
 595  *            0  Message wasn't logged
 596  *          num  Number of output characters
 597  */
 598 int ntfs_log_handler_stderr(const char *function, const char *file,
 599         int line, u32 level, void *data, const char *format, va_list args)
 600 {
 601         if (!data)
 602                 data = stderr;
 603 
 604         return ntfs_log_handler_fprintf(function, file, line, level, data, format, args);
 605 }
 606 
 607 
 608 /**
 609  * ntfs_log_parse_option - Act upon command line options
 610  * @option:     Option flag
 611  *
 612  * Delegate some of the work of parsing the command line.  All the options begin
 613  * with "--log-".  Options cause log levels to be enabled in @ntfs_log (the
 614  * global logging structure).
 615  *
 616  * Note: The "colour" option changes the logging handler.
 617  *
 618  * Returns:  TRUE  Option understood
 619  *          FALSE  Invalid log option
 620  */
 621 BOOL ntfs_log_parse_option(const char *option)
 622 {
 623         if (strcmp(option, "--log-debug") == 0) {
 624                 ntfs_log_set_levels(NTFS_LOG_LEVEL_DEBUG);
 625                 return TRUE;
 626         } else if (strcmp(option, "--log-verbose") == 0) {
 627                 ntfs_log_set_levels(NTFS_LOG_LEVEL_VERBOSE);
 628                 return TRUE;
 629         } else if (strcmp(option, "--log-quiet") == 0) {
 630                 ntfs_log_clear_levels(NTFS_LOG_LEVEL_QUIET);
 631                 return TRUE;
 632         } else if (strcmp(option, "--log-trace") == 0) {
 633                 ntfs_log_set_levels(NTFS_LOG_LEVEL_TRACE);
 634                 return TRUE;
 635         } else if ((strcmp(option, "--log-colour") == 0) ||
 636                    (strcmp(option, "--log-color") == 0)) {
 637                 ntfs_log_set_flags(NTFS_LOG_FLAG_COLOUR);
 638                 return TRUE;
 639         }
 640 
 641         ntfs_log_debug("Unknown logging option '%s'\n", option);
 642         return FALSE;
 643 }
 644