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 54 static 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 */ 70 struct 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 */ 80 static 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 */ 104 u32 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 */ 118 u32 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 */ 135 u32 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 */ 151 u32 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 */ 165 u32 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 */ 182 u32 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 #ifndef __HAIKU__ 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 */ 200 static 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 */ 236 static 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 #endif 279 280 281 /** 282 * ntfs_log_set_handler - Provide an alternate logging handler 283 * @handler: function to perform the logging 284 * 285 * This alternate handler will be called for all future logging requests. 286 * If no @handler is specified, logging will revert to the default handler. 287 */ 288 void ntfs_log_set_handler(ntfs_log_handler *handler) 289 { 290 if (handler) { 291 ntfs_log.handler = handler; 292 #ifdef HAVE_SYSLOG_H 293 if (handler == ntfs_log_handler_syslog) 294 openlog("ntfs-3g", LOG_PID, LOG_USER); 295 #endif 296 } else 297 ntfs_log.handler = ntfs_log_handler_null; 298 } 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 #ifndef __HAIKU__ 394 /** 395 * ntfs_log_handler_fprintf - Basic logging handler 396 * @function: Function in which the log line occurred 397 * @file: File in which the log line occurred 398 * @line: Line number on which the log line occurred 399 * @level: Level at which the line is logged 400 * @data: User specified data, possibly specific to a handler 401 * @format: printf-style formatting string 402 * @args: Arguments to be formatted 403 * 404 * A simple logging handler. This is where the log line is finally displayed. 405 * It is more likely that you will want to set the handler to either 406 * ntfs_log_handler_outerr or ntfs_log_handler_stderr. 407 * 408 * Note: For this handler, @data is a pointer to a FILE output stream. 409 * If @data is NULL, nothing will be displayed. 410 * 411 * Returns: -1 Error occurred 412 * 0 Message wasn't logged 413 * num Number of output characters 414 */ 415 int ntfs_log_handler_fprintf(const char *function, const char *file, 416 int line, u32 level, void *data, const char *format, va_list args) 417 { 418 #ifdef DEBUG 419 int i; 420 #endif 421 int ret = 0; 422 int olderr = errno; 423 FILE *stream; 424 425 if (!data) /* Interpret data as a FILE stream. */ 426 return 0; /* If it's NULL, we can't do anything. */ 427 stream = (FILE*)data; 428 429 #ifdef DEBUG 430 if (level == NTFS_LOG_LEVEL_LEAVE) { 431 if (tab) 432 tab--; 433 return 0; 434 } 435 436 for (i = 0; i < tab; i++) 437 ret += fprintf(stream, " "); 438 #endif 439 if ((ntfs_log.flags & NTFS_LOG_FLAG_ONLYNAME) && 440 (strchr(file, PATH_SEP))) /* Abbreviate the filename */ 441 file = strrchr(file, PATH_SEP) + 1; 442 443 if (ntfs_log.flags & NTFS_LOG_FLAG_PREFIX) /* Prefix the output */ 444 ret += fprintf(stream, "%s", ntfs_log_get_prefix(level)); 445 446 if (ntfs_log.flags & NTFS_LOG_FLAG_FILENAME) /* Source filename */ 447 ret += fprintf(stream, "%s ", file); 448 449 if (ntfs_log.flags & NTFS_LOG_FLAG_LINE) /* Source line number */ 450 ret += fprintf(stream, "(%d) ", line); 451 452 if ((ntfs_log.flags & NTFS_LOG_FLAG_FUNCTION) || /* Source function */ 453 (level & NTFS_LOG_LEVEL_TRACE) || (level & NTFS_LOG_LEVEL_ENTER)) 454 ret += fprintf(stream, "%s(): ", function); 455 456 ret += vfprintf(stream, format, args); 457 458 if (level & NTFS_LOG_LEVEL_PERROR) 459 ret += fprintf(stream, ": %s\n", strerror(olderr)); 460 461 #ifdef DEBUG 462 if (level == NTFS_LOG_LEVEL_ENTER) 463 tab++; 464 #endif 465 fflush(stream); 466 errno = olderr; 467 return ret; 468 } 469 470 #endif // __HAIKU__ 471 472 /** 473 * ntfs_log_handler_null - Null logging handler (no output) 474 * @function: Function in which the log line occurred 475 * @file: File in which the log line occurred 476 * @line: Line number on which the log line occurred 477 * @level: Level at which the line is logged 478 * @data: User specified data, possibly specific to a handler 479 * @format: printf-style formatting string 480 * @args: Arguments to be formatted 481 * 482 * This handler produces no output. It provides a way to temporarily disable 483 * logging, without having to change the levels and flags. 484 * 485 * Returns: 0 Message wasn't logged 486 */ 487 int ntfs_log_handler_null(const char *function __attribute__((unused)), const char *file __attribute__((unused)), 488 int line __attribute__((unused)), u32 level __attribute__((unused)), void *data __attribute__((unused)), 489 const char *format __attribute__((unused)), va_list args __attribute__((unused))) 490 { 491 return 0; 492 } 493 494 #ifndef __HAIKU__ 495 496 /** 497 * ntfs_log_handler_stdout - All logs go to stdout 498 * @function: Function in which the log line occurred 499 * @file: File in which the log line occurred 500 * @line: Line number on which the log line occurred 501 * @level: Level at which the line is logged 502 * @data: User specified data, possibly specific to a handler 503 * @format: printf-style formatting string 504 * @args: Arguments to be formatted 505 * 506 * Display a log message to stdout. 507 * 508 * Note: For this handler, @data is a pointer to a FILE output stream. 509 * If @data is NULL, then stdout will be used. 510 * 511 * Note: This function calls ntfs_log_handler_fprintf to do the main work. 512 * 513 * Returns: -1 Error occurred 514 * 0 Message wasn't logged 515 * num Number of output characters 516 */ 517 int ntfs_log_handler_stdout(const char *function, const char *file, 518 int line, u32 level, void *data, const char *format, va_list args) 519 { 520 if (!data) 521 data = stdout; 522 523 return ntfs_log_handler_fprintf(function, file, line, level, data, format, args); 524 } 525 526 /** 527 * ntfs_log_handler_outerr - Logs go to stdout/stderr depending on level 528 * @function: Function in which the log line occurred 529 * @file: File in which the log line occurred 530 * @line: Line number on which the log line occurred 531 * @level: Level at which the line is logged 532 * @data: User specified data, possibly specific to a handler 533 * @format: printf-style formatting string 534 * @args: Arguments to be formatted 535 * 536 * Display a log message. The output stream will be determined by the log 537 * level. 538 * 539 * Note: For this handler, @data is a pointer to a FILE output stream. 540 * If @data is NULL, the function ntfs_log_get_stream will be called 541 * 542 * Note: This function calls ntfs_log_handler_fprintf to do the main work. 543 * 544 * Returns: -1 Error occurred 545 * 0 Message wasn't logged 546 * num Number of output characters 547 */ 548 int ntfs_log_handler_outerr(const char *function, const char *file, 549 int line, u32 level, void *data, const char *format, va_list args) 550 { 551 if (!data) 552 data = ntfs_log_get_stream(level); 553 554 return ntfs_log_handler_fprintf(function, file, line, level, data, format, args); 555 } 556 557 /** 558 * ntfs_log_handler_stderr - All logs go to stderr 559 * @function: Function in which the log line occurred 560 * @file: File in which the log line occurred 561 * @line: Line number on which the log line occurred 562 * @level: Level at which the line is logged 563 * @data: User specified data, possibly specific to a handler 564 * @format: printf-style formatting string 565 * @args: Arguments to be formatted 566 * 567 * Display a log message to stderr. 568 * 569 * Note: For this handler, @data is a pointer to a FILE output stream. 570 * If @data is NULL, then stdout will be used. 571 * 572 * Note: This function calls ntfs_log_handler_fprintf to do the main work. 573 * 574 * Returns: -1 Error occurred 575 * 0 Message wasn't logged 576 * num Number of output characters 577 */ 578 int ntfs_log_handler_stderr(const char *function, const char *file, 579 int line, u32 level, void *data, const char *format, va_list args) 580 { 581 if (!data) 582 data = stderr; 583 584 return ntfs_log_handler_fprintf(function, file, line, level, data, format, args); 585 } 586 587 #endif // __HAIKU__ 588 589 590 /** 591 * ntfs_log_parse_option - Act upon command line options 592 * @option: Option flag 593 * 594 * Delegate some of the work of parsing the command line. All the options begin 595 * with "--log-". Options cause log levels to be enabled in @ntfs_log (the 596 * global logging structure). 597 * 598 * Note: The "colour" option changes the logging handler. 599 * 600 * Returns: TRUE Option understood 601 * FALSE Invalid log option 602 */ 603 BOOL ntfs_log_parse_option(const char *option) 604 { 605 if (strcmp(option, "--log-debug") == 0) { 606 ntfs_log_set_levels(NTFS_LOG_LEVEL_DEBUG); 607 return TRUE; 608 } else if (strcmp(option, "--log-verbose") == 0) { 609 ntfs_log_set_levels(NTFS_LOG_LEVEL_VERBOSE); 610 return TRUE; 611 } else if (strcmp(option, "--log-quiet") == 0) { 612 ntfs_log_clear_levels(NTFS_LOG_LEVEL_QUIET); 613 return TRUE; 614 } else if (strcmp(option, "--log-trace") == 0) { 615 ntfs_log_set_levels(NTFS_LOG_LEVEL_TRACE); 616 return TRUE; 617 } 618 619 ntfs_log_debug("Unknown logging option '%s'\n", option); 620 return FALSE; 621 } 622 623