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