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 */
ntfs_log_get_levels(void)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 */
ntfs_log_set_levels(u32 levels)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 */
ntfs_log_clear_levels(u32 levels)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 */
ntfs_log_get_flags(void)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 */
ntfs_log_set_flags(u32 flags)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 */
ntfs_log_clear_flags(u32 flags)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__
ntfs_log_get_stream(u32 level)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 */
ntfs_log_get_prefix(u32 level)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 */
ntfs_log_set_handler(ntfs_log_handler * handler)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 */
ntfs_log_redirect(const char * function,const char * file,int line,u32 level,void * data,const char * format,...)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
ntfs_log_handler_syslog(const char * function,const char * file,int line,u32 level,void * data,const char * format,va_list args)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
ntfs_log_early_error(const char * format,...)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 */
ntfs_log_handler_fprintf(const char * function,const char * file,int line,u32 level,void * data,const char * format,va_list args)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 */
ntfs_log_handler_null(const char * function,const char * file,int line,u32 level,void * data,const char * format,va_list args)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 */
ntfs_log_handler_stdout(const char * function,const char * file,int line,u32 level,void * data,const char * format,va_list args)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 */
ntfs_log_handler_outerr(const char * function,const char * file,int line,u32 level,void * data,const char * format,va_list args)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 */
ntfs_log_handler_stderr(const char * function,const char * file,int line,u32 level,void * data,const char * format,va_list args)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 */
ntfs_log_parse_option(const char * option)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