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