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