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 55static 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 */ 71struct 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 */ 81static 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 */ 105u32 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 */ 119u32 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 */ 136u32 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 */ 152u32 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 */ 166u32 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 */ 183u32 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 */ 201static 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 */ 237static 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 */ 289void 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 */ 318int 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 360int 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); 387out: 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 400void 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 */ 440int 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 */ 512int 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 */ 542int 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 */ 573int 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 */ 603int 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 */ 627BOOL 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