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