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 #endif 230 231 /** 232 * ntfs_log_get_prefix - Default prefixes for logging levels 233 * @level: Log level to be prefixed 234 * 235 * Prefixing the logging output can make it easier to parse. 236 * 237 * Returns: "string" Prefix to be used 238 */ 239 static const char * ntfs_log_get_prefix(u32 level) 240 { 241 const char *prefix; 242 243 switch (level) { 244 case NTFS_LOG_LEVEL_DEBUG: 245 prefix = "DEBUG: "; 246 break; 247 case NTFS_LOG_LEVEL_TRACE: 248 prefix = "TRACE: "; 249 break; 250 case NTFS_LOG_LEVEL_QUIET: 251 prefix = "QUIET: "; 252 break; 253 case NTFS_LOG_LEVEL_INFO: 254 prefix = "INFO: "; 255 break; 256 case NTFS_LOG_LEVEL_VERBOSE: 257 prefix = "VERBOSE: "; 258 break; 259 case NTFS_LOG_LEVEL_PROGRESS: 260 prefix = "PROGRESS: "; 261 break; 262 case NTFS_LOG_LEVEL_WARNING: 263 prefix = "WARNING: "; 264 break; 265 case NTFS_LOG_LEVEL_ERROR: 266 prefix = "ERROR: "; 267 break; 268 case NTFS_LOG_LEVEL_PERROR: 269 prefix = "ERROR: "; 270 break; 271 case NTFS_LOG_LEVEL_CRITICAL: 272 prefix = "CRITICAL: "; 273 break; 274 default: 275 prefix = ""; 276 break; 277 } 278 279 return prefix; 280 } 281 282 283 /** 284 * ntfs_log_set_handler - Provide an alternate logging handler 285 * @handler: function to perform the logging 286 * 287 * This alternate handler will be called for all future logging requests. 288 * If no @handler is specified, logging will revert to the default handler. 289 */ 290 void ntfs_log_set_handler(ntfs_log_handler *handler) 291 { 292 if (handler) { 293 ntfs_log.handler = handler; 294 #ifdef HAVE_SYSLOG_H 295 if (handler == ntfs_log_handler_syslog) 296 openlog("ntfs-3g", LOG_PID, LOG_USER); 297 #endif 298 } else 299 ntfs_log.handler = ntfs_log_handler_null; 300 } 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 va_list args; 404 #ifdef __HAIKU__ 405 char buffer[1024]; 406 #endif 407 408 va_start(args, format); 409 #ifdef HAVE_SYSLOG_H 410 openlog("ntfs-3g", LOG_PID, LOG_USER); 411 ntfs_log_handler_syslog(NULL, NULL, 0, 412 NTFS_LOG_LEVEL_ERROR, NULL, 413 format, args); 414 #elif defined(__HAIKU__) 415 vsnprintf(buffer, sizeof(buffer) - 1, format, args); 416 dprintf("%s", buffer); 417 #else 418 vfprintf(stderr,format,args); 419 #endif 420 va_end(args); 421 } 422 423 /** 424 * ntfs_log_handler_fprintf - Basic logging handler 425 * @function: Function in which the log line occurred 426 * @file: File in which the log line occurred 427 * @line: Line number on which the log line occurred 428 * @level: Level at which the line is logged 429 * @data: User specified data, possibly specific to a handler 430 * @format: printf-style formatting string 431 * @args: Arguments to be formatted 432 * 433 * A simple logging handler. This is where the log line is finally displayed. 434 * It is more likely that you will want to set the handler to either 435 * ntfs_log_handler_outerr or ntfs_log_handler_stderr. 436 * 437 * Note: For this handler, @data is a pointer to a FILE output stream. 438 * If @data is NULL, nothing will be displayed. 439 * 440 * Returns: -1 Error occurred 441 * 0 Message wasn't logged 442 * num Number of output characters 443 */ 444 int ntfs_log_handler_fprintf(const char *function, const char *file, 445 int line, u32 level, void *data, const char *format, va_list args) 446 { 447 #ifdef DEBUG 448 int i; 449 #endif 450 int ret = 0; 451 int olderr = errno; 452 453 #ifndef __HAIKU__ 454 FILE *stream; 455 456 if (!data) /* Interpret data as a FILE stream. */ 457 return 0; /* If it's NULL, we can't do anything. */ 458 stream = (FILE*)data; 459 #else 460 #define fprintf(stream, ...) dprintf(__VA_ARGS__) 461 char buffer[1024]; 462 #endif 463 464 #ifdef DEBUG 465 if (level == NTFS_LOG_LEVEL_LEAVE) { 466 if (tab) 467 tab--; 468 return 0; 469 } 470 471 for (i = 0; i < tab; i++) 472 ret += fprintf(stream, " "); 473 #endif 474 if ((ntfs_log.flags & NTFS_LOG_FLAG_ONLYNAME) && 475 (strchr(file, PATH_SEP))) /* Abbreviate the filename */ 476 file = strrchr(file, PATH_SEP) + 1; 477 478 if (ntfs_log.flags & NTFS_LOG_FLAG_PREFIX) /* Prefix the output */ 479 ret += fprintf(stream, "%s", ntfs_log_get_prefix(level)); 480 481 if (ntfs_log.flags & NTFS_LOG_FLAG_FILENAME) /* Source filename */ 482 ret += fprintf(stream, "%s ", file); 483 484 if (ntfs_log.flags & NTFS_LOG_FLAG_LINE) /* Source line number */ 485 ret += fprintf(stream, "(%d) ", line); 486 487 if ((ntfs_log.flags & NTFS_LOG_FLAG_FUNCTION) || /* Source function */ 488 (level & NTFS_LOG_LEVEL_TRACE) || (level & NTFS_LOG_LEVEL_ENTER)) 489 ret += fprintf(stream, "%s(): ", function); 490 491 #ifndef __HAIKU__ 492 ret += vfprintf(stream, format, args); 493 #else 494 ret += vsnprintf(buffer, sizeof(buffer), format, args); 495 dprintf("%s", buffer); 496 #endif 497 498 if (level & NTFS_LOG_LEVEL_PERROR) 499 ret += fprintf(stream, ": %s\n", strerror(olderr)); 500 501 #ifdef DEBUG 502 if (level == NTFS_LOG_LEVEL_ENTER) 503 tab++; 504 #endif 505 #ifndef __HAIKU__ 506 fflush(stream); 507 #else 508 #undef fprintf 509 #endif 510 errno = olderr; 511 return ret; 512 } 513 514 /** 515 * ntfs_log_handler_null - Null logging handler (no output) 516 * @function: Function in which the log line occurred 517 * @file: File in which the log line occurred 518 * @line: Line number on which the log line occurred 519 * @level: Level at which the line is logged 520 * @data: User specified data, possibly specific to a handler 521 * @format: printf-style formatting string 522 * @args: Arguments to be formatted 523 * 524 * This handler produces no output. It provides a way to temporarily disable 525 * logging, without having to change the levels and flags. 526 * 527 * Returns: 0 Message wasn't logged 528 */ 529 int ntfs_log_handler_null(const char *function __attribute__((unused)), const char *file __attribute__((unused)), 530 int line __attribute__((unused)), u32 level __attribute__((unused)), void *data __attribute__((unused)), 531 const char *format __attribute__((unused)), va_list args __attribute__((unused))) 532 { 533 return 0; 534 } 535 536 /** 537 * ntfs_log_handler_stdout - All logs go to stdout 538 * @function: Function in which the log line occurred 539 * @file: File in which the log line occurred 540 * @line: Line number on which the log line occurred 541 * @level: Level at which the line is logged 542 * @data: User specified data, possibly specific to a handler 543 * @format: printf-style formatting string 544 * @args: Arguments to be formatted 545 * 546 * Display a log message to stdout. 547 * 548 * Note: For this handler, @data is a pointer to a FILE output stream. 549 * If @data is NULL, then stdout will be used. 550 * 551 * Note: This function calls ntfs_log_handler_fprintf to do the main work. 552 * 553 * Returns: -1 Error occurred 554 * 0 Message wasn't logged 555 * num Number of output characters 556 */ 557 int ntfs_log_handler_stdout(const char *function, const char *file, 558 int line, u32 level, void *data, const char *format, va_list args) 559 { 560 #ifndef __HAIKU__ 561 if (!data) 562 data = stdout; 563 #endif 564 565 return ntfs_log_handler_fprintf(function, file, line, level, data, format, args); 566 } 567 568 /** 569 * ntfs_log_handler_outerr - Logs go to stdout/stderr depending on level 570 * @function: Function in which the log line occurred 571 * @file: File in which the log line occurred 572 * @line: Line number on which the log line occurred 573 * @level: Level at which the line is logged 574 * @data: User specified data, possibly specific to a handler 575 * @format: printf-style formatting string 576 * @args: Arguments to be formatted 577 * 578 * Display a log message. The output stream will be determined by the log 579 * level. 580 * 581 * Note: For this handler, @data is a pointer to a FILE output stream. 582 * If @data is NULL, the function ntfs_log_get_stream will be called 583 * 584 * Note: This function calls ntfs_log_handler_fprintf to do the main work. 585 * 586 * Returns: -1 Error occurred 587 * 0 Message wasn't logged 588 * num Number of output characters 589 */ 590 int ntfs_log_handler_outerr(const char *function, const char *file, 591 int line, u32 level, void *data, const char *format, va_list args) 592 { 593 #ifndef __HAIKU__ 594 if (!data) 595 data = ntfs_log_get_stream(level); 596 #endif 597 598 return ntfs_log_handler_fprintf(function, file, line, level, data, format, args); 599 } 600 601 /** 602 * ntfs_log_handler_stderr - All logs go to stderr 603 * @function: Function in which the log line occurred 604 * @file: File in which the log line occurred 605 * @line: Line number on which the log line occurred 606 * @level: Level at which the line is logged 607 * @data: User specified data, possibly specific to a handler 608 * @format: printf-style formatting string 609 * @args: Arguments to be formatted 610 * 611 * Display a log message to stderr. 612 * 613 * Note: For this handler, @data is a pointer to a FILE output stream. 614 * If @data is NULL, then stdout will be used. 615 * 616 * Note: This function calls ntfs_log_handler_fprintf to do the main work. 617 * 618 * Returns: -1 Error occurred 619 * 0 Message wasn't logged 620 * num Number of output characters 621 */ 622 int ntfs_log_handler_stderr(const char *function, const char *file, 623 int line, u32 level, void *data, const char *format, va_list args) 624 { 625 #ifndef __HAIKU__ 626 if (!data) 627 data = stderr; 628 #endif 629 630 return ntfs_log_handler_fprintf(function, file, line, level, data, format, args); 631 } 632 633 634 /** 635 * ntfs_log_parse_option - Act upon command line options 636 * @option: Option flag 637 * 638 * Delegate some of the work of parsing the command line. All the options begin 639 * with "--log-". Options cause log levels to be enabled in @ntfs_log (the 640 * global logging structure). 641 * 642 * Note: The "colour" option changes the logging handler. 643 * 644 * Returns: TRUE Option understood 645 * FALSE Invalid log option 646 */ 647 BOOL ntfs_log_parse_option(const char *option) 648 { 649 if (strcmp(option, "--log-debug") == 0) { 650 ntfs_log_set_levels(NTFS_LOG_LEVEL_DEBUG); 651 return TRUE; 652 } else if (strcmp(option, "--log-verbose") == 0) { 653 ntfs_log_set_levels(NTFS_LOG_LEVEL_VERBOSE); 654 return TRUE; 655 } else if (strcmp(option, "--log-quiet") == 0) { 656 ntfs_log_clear_levels(NTFS_LOG_LEVEL_QUIET); 657 return TRUE; 658 } else if (strcmp(option, "--log-trace") == 0) { 659 ntfs_log_set_levels(NTFS_LOG_LEVEL_TRACE); 660 return TRUE; 661 } 662 663 ntfs_log_debug("Unknown logging option '%s'\n", option); 664 return FALSE; 665 } 666 667