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