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 #ifndef __HAIKU__ 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 static FILE * ntfs_log_get_stream(u32 level) 202 { 203 FILE *stream; 204 205 switch (level) { 206 case NTFS_LOG_LEVEL_INFO: 207 case NTFS_LOG_LEVEL_QUIET: 208 case NTFS_LOG_LEVEL_PROGRESS: 209 case NTFS_LOG_LEVEL_VERBOSE: 210 stream = stdout; 211 break; 212 213 case NTFS_LOG_LEVEL_DEBUG: 214 case NTFS_LOG_LEVEL_TRACE: 215 case NTFS_LOG_LEVEL_ENTER: 216 case NTFS_LOG_LEVEL_LEAVE: 217 case NTFS_LOG_LEVEL_WARNING: 218 case NTFS_LOG_LEVEL_ERROR: 219 case NTFS_LOG_LEVEL_CRITICAL: 220 case NTFS_LOG_LEVEL_PERROR: 221 default: 222 stream = stderr; 223 break; 224 } 225 226 return stream; 227 } 228 229 /** 230 * ntfs_log_get_prefix - Default prefixes for logging levels 231 * @level: Log level to be prefixed 232 * 233 * Prefixing the logging output can make it easier to parse. 234 * 235 * Returns: "string" Prefix to be used 236 */ 237 static const char * ntfs_log_get_prefix(u32 level) 238 { 239 const char *prefix; 240 241 switch (level) { 242 case NTFS_LOG_LEVEL_DEBUG: 243 prefix = "DEBUG: "; 244 break; 245 case NTFS_LOG_LEVEL_TRACE: 246 prefix = "TRACE: "; 247 break; 248 case NTFS_LOG_LEVEL_QUIET: 249 prefix = "QUIET: "; 250 break; 251 case NTFS_LOG_LEVEL_INFO: 252 prefix = "INFO: "; 253 break; 254 case NTFS_LOG_LEVEL_VERBOSE: 255 prefix = "VERBOSE: "; 256 break; 257 case NTFS_LOG_LEVEL_PROGRESS: 258 prefix = "PROGRESS: "; 259 break; 260 case NTFS_LOG_LEVEL_WARNING: 261 prefix = "WARNING: "; 262 break; 263 case NTFS_LOG_LEVEL_ERROR: 264 prefix = "ERROR: "; 265 break; 266 case NTFS_LOG_LEVEL_PERROR: 267 prefix = "ERROR: "; 268 break; 269 case NTFS_LOG_LEVEL_CRITICAL: 270 prefix = "CRITICAL: "; 271 break; 272 default: 273 prefix = ""; 274 break; 275 } 276 277 return prefix; 278 } 279 #endif 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 301 /** 302 * ntfs_log_redirect - Pass on the request to the real handler 303 * @function: Function in which the log line occurred 304 * @file: File in which the log line occurred 305 * @line: Line number on which the log line occurred 306 * @level: Level at which the line is logged 307 * @data: User specified data, possibly specific to a handler 308 * @format: printf-style formatting string 309 * @...: Arguments to be formatted 310 * 311 * This is just a redirector function. The arguments are simply passed to the 312 * main logging handler (as defined in the global logging struct @ntfs_log). 313 * 314 * Returns: -1 Error occurred 315 * 0 Message wasn't logged 316 * num Number of output characters 317 */ 318 int ntfs_log_redirect(const char *function, const char *file, 319 int line, u32 level, void *data, const char *format, ...) 320 { 321 int olderr = errno; 322 int ret; 323 va_list args; 324 325 if (!(ntfs_log.levels & level)) /* Don't log this message */ 326 return 0; 327 328 va_start(args, format); 329 errno = olderr; 330 ret = ntfs_log.handler(function, file, line, level, data, format, args); 331 va_end(args); 332 333 errno = olderr; 334 return ret; 335 } 336 337 338 /** 339 * ntfs_log_handler_syslog - syslog logging handler 340 * @function: Function in which the log line occurred 341 * @file: File in which the log line occurred 342 * @line: Line number on which the log line occurred 343 * @level: Level at which the line is logged 344 * @data: User specified data, possibly specific to a handler 345 * @format: printf-style formatting string 346 * @args: Arguments to be formatted 347 * 348 * A simple syslog logging handler. Ignores colors. 349 * 350 * Returns: -1 Error occurred 351 * 0 Message wasn't logged 352 * num Number of output characters 353 */ 354 355 356 #ifdef HAVE_SYSLOG_H 357 358 #define LOG_LINE_LEN 512 359 360 int ntfs_log_handler_syslog(const char *function __attribute__((unused)), 361 const char *file __attribute__((unused)), 362 int line __attribute__((unused)), u32 level, 363 void *data __attribute__((unused)), 364 const char *format, va_list args) 365 { 366 char logbuf[LOG_LINE_LEN]; 367 int ret, olderr = errno; 368 369 #ifndef DEBUG 370 if ((level & NTFS_LOG_LEVEL_PERROR) && errno == ENOSPC) 371 return 1; 372 #endif 373 ret = vsnprintf(logbuf, LOG_LINE_LEN, format, args); 374 if (ret < 0) { 375 vsyslog(LOG_NOTICE, format, args); 376 ret = 1; 377 goto out; 378 } 379 380 if ((LOG_LINE_LEN > ret + 3) && (level & NTFS_LOG_LEVEL_PERROR)) { 381 strncat(logbuf, ": ", LOG_LINE_LEN - ret - 1); 382 strncat(logbuf, strerror(olderr), LOG_LINE_LEN - (ret + 3)); 383 ret = strlen(logbuf); 384 } 385 386 syslog(LOG_NOTICE, "%s", logbuf); 387 out: 388 errno = olderr; 389 return ret; 390 } 391 #endif 392 393 /* 394 * Early logging before the logs are redirected 395 * 396 * (not quite satisfactory : this appears before the ntfs-g banner, 397 * and with a different pid) 398 */ 399 400 void ntfs_log_early_error(const char *format, ...) 401 { 402 #ifndef __HAIKU__ 403 va_list args; 404 405 va_start(args, format); 406 #ifdef HAVE_SYSLOG_H 407 openlog("ntfs-3g", LOG_PID, LOG_USER); 408 ntfs_log_handler_syslog(NULL, NULL, 0, 409 NTFS_LOG_LEVEL_ERROR, NULL, 410 format, args); 411 #else 412 vfprintf(stderr,format,args); 413 #endif 414 va_end(args); 415 #endif 416 } 417 418 #ifndef __HAIKU__ 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 int ntfs_log_handler_fprintf(const char *function, const char *file, 441 int line, u32 level, void *data, const char *format, va_list args) 442 { 443 #ifdef DEBUG 444 int i; 445 #endif 446 int ret = 0; 447 int olderr = errno; 448 FILE *stream; 449 450 if (!data) /* Interpret data as a FILE stream. */ 451 return 0; /* If it's NULL, we can't do anything. */ 452 stream = (FILE*)data; 453 454 #ifdef DEBUG 455 if (level == NTFS_LOG_LEVEL_LEAVE) { 456 if (tab) 457 tab--; 458 return 0; 459 } 460 461 for (i = 0; i < tab; i++) 462 ret += fprintf(stream, " "); 463 #endif 464 if ((ntfs_log.flags & NTFS_LOG_FLAG_ONLYNAME) && 465 (strchr(file, PATH_SEP))) /* Abbreviate the filename */ 466 file = strrchr(file, PATH_SEP) + 1; 467 468 if (ntfs_log.flags & NTFS_LOG_FLAG_PREFIX) /* Prefix the output */ 469 ret += fprintf(stream, "%s", ntfs_log_get_prefix(level)); 470 471 if (ntfs_log.flags & NTFS_LOG_FLAG_FILENAME) /* Source filename */ 472 ret += fprintf(stream, "%s ", file); 473 474 if (ntfs_log.flags & NTFS_LOG_FLAG_LINE) /* Source line number */ 475 ret += fprintf(stream, "(%d) ", line); 476 477 if ((ntfs_log.flags & NTFS_LOG_FLAG_FUNCTION) || /* Source function */ 478 (level & NTFS_LOG_LEVEL_TRACE) || (level & NTFS_LOG_LEVEL_ENTER)) 479 ret += fprintf(stream, "%s(): ", function); 480 481 ret += vfprintf(stream, format, args); 482 483 if (level & NTFS_LOG_LEVEL_PERROR) 484 ret += fprintf(stream, ": %s\n", strerror(olderr)); 485 486 #ifdef DEBUG 487 if (level == NTFS_LOG_LEVEL_ENTER) 488 tab++; 489 #endif 490 fflush(stream); 491 errno = olderr; 492 return ret; 493 } 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 #ifndef __HAIKU__ 520 521 /** 522 * ntfs_log_handler_stdout - All logs go to stdout 523 * @function: Function in which the log line occurred 524 * @file: File in which the log line occurred 525 * @line: Line number on which the log line occurred 526 * @level: Level at which the line is logged 527 * @data: User specified data, possibly specific to a handler 528 * @format: printf-style formatting string 529 * @args: Arguments to be formatted 530 * 531 * Display a log message to stdout. 532 * 533 * Note: For this handler, @data is a pointer to a FILE output stream. 534 * If @data is NULL, then stdout will be used. 535 * 536 * Note: This function calls ntfs_log_handler_fprintf to do the main work. 537 * 538 * Returns: -1 Error occurred 539 * 0 Message wasn't logged 540 * num Number of output characters 541 */ 542 int ntfs_log_handler_stdout(const char *function, const char *file, 543 int line, u32 level, void *data, const char *format, va_list args) 544 { 545 if (!data) 546 data = stdout; 547 548 return ntfs_log_handler_fprintf(function, file, line, level, data, format, args); 549 } 550 551 /** 552 * ntfs_log_handler_outerr - Logs go to stdout/stderr depending on level 553 * @function: Function in which the log line occurred 554 * @file: File in which the log line occurred 555 * @line: Line number on which the log line occurred 556 * @level: Level at which the line is logged 557 * @data: User specified data, possibly specific to a handler 558 * @format: printf-style formatting string 559 * @args: Arguments to be formatted 560 * 561 * Display a log message. The output stream will be determined by the log 562 * level. 563 * 564 * Note: For this handler, @data is a pointer to a FILE output stream. 565 * If @data is NULL, the function ntfs_log_get_stream will be called 566 * 567 * Note: This function calls ntfs_log_handler_fprintf to do the main work. 568 * 569 * Returns: -1 Error occurred 570 * 0 Message wasn't logged 571 * num Number of output characters 572 */ 573 int ntfs_log_handler_outerr(const char *function, const char *file, 574 int line, u32 level, void *data, const char *format, va_list args) 575 { 576 if (!data) 577 data = ntfs_log_get_stream(level); 578 579 return ntfs_log_handler_fprintf(function, file, line, level, data, format, args); 580 } 581 582 /** 583 * ntfs_log_handler_stderr - All logs go to stderr 584 * @function: Function in which the log line occurred 585 * @file: File in which the log line occurred 586 * @line: Line number on which the log line occurred 587 * @level: Level at which the line is logged 588 * @data: User specified data, possibly specific to a handler 589 * @format: printf-style formatting string 590 * @args: Arguments to be formatted 591 * 592 * Display a log message to stderr. 593 * 594 * Note: For this handler, @data is a pointer to a FILE output stream. 595 * If @data is NULL, then stdout will be used. 596 * 597 * Note: This function calls ntfs_log_handler_fprintf to do the main work. 598 * 599 * Returns: -1 Error occurred 600 * 0 Message wasn't logged 601 * num Number of output characters 602 */ 603 int ntfs_log_handler_stderr(const char *function, const char *file, 604 int line, u32 level, void *data, const char *format, va_list args) 605 { 606 if (!data) 607 data = stderr; 608 609 return ntfs_log_handler_fprintf(function, file, line, level, data, format, args); 610 } 611 612 #endif // __HAIKU__ 613 614 /** 615 * ntfs_log_parse_option - Act upon command line options 616 * @option: Option flag 617 * 618 * Delegate some of the work of parsing the command line. All the options begin 619 * with "--log-". Options cause log levels to be enabled in @ntfs_log (the 620 * global logging structure). 621 * 622 * Note: The "colour" option changes the logging handler. 623 * 624 * Returns: TRUE Option understood 625 * FALSE Invalid log option 626 */ 627 BOOL ntfs_log_parse_option(const char *option) 628 { 629 if (strcmp(option, "--log-debug") == 0) { 630 ntfs_log_set_levels(NTFS_LOG_LEVEL_DEBUG); 631 return TRUE; 632 } else if (strcmp(option, "--log-verbose") == 0) { 633 ntfs_log_set_levels(NTFS_LOG_LEVEL_VERBOSE); 634 return TRUE; 635 } else if (strcmp(option, "--log-quiet") == 0) { 636 ntfs_log_clear_levels(NTFS_LOG_LEVEL_QUIET); 637 return TRUE; 638 } else if (strcmp(option, "--log-trace") == 0) { 639 ntfs_log_set_levels(NTFS_LOG_LEVEL_TRACE); 640 return TRUE; 641 } 642 643 ntfs_log_debug("Unknown logging option '%s'\n", option); 644 return FALSE; 645 } 646 647