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