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 */
ntfs_log_get_levels(void)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 */
ntfs_log_set_levels(u32 levels)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 */
ntfs_log_clear_levels(u32 levels)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 */
ntfs_log_get_flags(void)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 */
ntfs_log_set_flags(u32 flags)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 */
ntfs_log_clear_flags(u32 flags)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
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 */
ntfs_log_get_stream(u32 level)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 */
ntfs_log_get_prefix(u32 level)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
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 */
ntfs_log_set_handler(ntfs_log_handler * handler)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 * ntfs_log_redirect - Pass on the request to the real handler
302 * @function: Function in which the log line occurred
303 * @file: File in which the log line occurred
304 * @line: Line number on which the log line occurred
305 * @level: Level at which the line is logged
306 * @data: User specified data, possibly specific to a handler
307 * @format: printf-style formatting string
308 * @...: Arguments to be formatted
309 *
310 * This is just a redirector function. The arguments are simply passed to the
311 * main logging handler (as defined in the global logging struct @ntfs_log).
312 *
313 * Returns: -1 Error occurred
314 * 0 Message wasn't logged
315 * num Number of output characters
316 */
ntfs_log_redirect(const char * function,const char * file,int line,u32 level,void * data,const char * format,...)317 int ntfs_log_redirect(const char *function, const char *file,
318 int line, u32 level, void *data, const char *format, ...)
319 {
320 int olderr = errno;
321 int ret;
322 va_list args;
323
324 if (!(ntfs_log.levels & level)) /* Don't log this message */
325 return 0;
326
327 va_start(args, format);
328 errno = olderr;
329 ret = ntfs_log.handler(function, file, line, level, data, format, args);
330 va_end(args);
331
332 errno = olderr;
333 return ret;
334 }
335
336
337 /**
338 * ntfs_log_handler_syslog - syslog logging handler
339 * @function: Function in which the log line occurred
340 * @file: File in which the log line occurred
341 * @line: Line number on which the log line occurred
342 * @level: Level at which the line is logged
343 * @data: User specified data, possibly specific to a handler
344 * @format: printf-style formatting string
345 * @args: Arguments to be formatted
346 *
347 * A simple syslog logging handler. Ignores colors.
348 *
349 * Returns: -1 Error occurred
350 * 0 Message wasn't logged
351 * num Number of output characters
352 */
353
354
355 #ifdef HAVE_SYSLOG_H
356
357 #define LOG_LINE_LEN 512
358
ntfs_log_handler_syslog(const char * function,const char * file,int line,u32 level,void * data,const char * format,va_list args)359 int ntfs_log_handler_syslog(const char *function __attribute__((unused)),
360 const char *file __attribute__((unused)),
361 int line __attribute__((unused)), u32 level,
362 void *data __attribute__((unused)),
363 const char *format, va_list args)
364 {
365 char logbuf[LOG_LINE_LEN];
366 int ret, olderr = errno;
367
368 #ifndef DEBUG
369 if ((level & NTFS_LOG_LEVEL_PERROR) && errno == ENOSPC)
370 return 1;
371 #endif
372 ret = vsnprintf(logbuf, LOG_LINE_LEN, format, args);
373 if (ret < 0) {
374 vsyslog(LOG_NOTICE, format, args);
375 ret = 1;
376 goto out;
377 }
378
379 if ((LOG_LINE_LEN > ret + 3) && (level & NTFS_LOG_LEVEL_PERROR)) {
380 strncat(logbuf, ": ", LOG_LINE_LEN - ret - 1);
381 strncat(logbuf, strerror(olderr), LOG_LINE_LEN - (ret + 3));
382 ret = strlen(logbuf);
383 }
384
385 syslog(LOG_NOTICE, "%s", logbuf);
386 out:
387 errno = olderr;
388 return ret;
389 }
390 #endif
391
392 /*
393 * Early logging before the logs are redirected
394 *
395 * (not quite satisfactory : this appears before the ntfs-g banner,
396 * and with a different pid)
397 */
398
ntfs_log_early_error(const char * format,...)399 void ntfs_log_early_error(const char *format, ...)
400 {
401 va_list args;
402
403 va_start(args, format);
404 #ifdef HAVE_SYSLOG_H
405 openlog("ntfs-3g", LOG_PID, LOG_USER);
406 ntfs_log_handler_syslog(NULL, NULL, 0,
407 NTFS_LOG_LEVEL_ERROR, NULL,
408 format, args);
409 #else
410 vfprintf(stderr,format,args);
411 #endif
412 va_end(args);
413 }
414
415 /**
416 * ntfs_log_handler_fprintf - Basic logging handler
417 * @function: Function in which the log line occurred
418 * @file: File in which the log line occurred
419 * @line: Line number on which the log line occurred
420 * @level: Level at which the line is logged
421 * @data: User specified data, possibly specific to a handler
422 * @format: printf-style formatting string
423 * @args: Arguments to be formatted
424 *
425 * A simple logging handler. This is where the log line is finally displayed.
426 * It is more likely that you will want to set the handler to either
427 * ntfs_log_handler_outerr or ntfs_log_handler_stderr.
428 *
429 * Note: For this handler, @data is a pointer to a FILE output stream.
430 * If @data is NULL, nothing will be displayed.
431 *
432 * Returns: -1 Error occurred
433 * 0 Message wasn't logged
434 * num Number of output characters
435 */
ntfs_log_handler_fprintf(const char * function,const char * file,int line,u32 level,void * data,const char * format,va_list args)436 int ntfs_log_handler_fprintf(const char *function, const char *file,
437 int line, u32 level, void *data, const char *format, va_list args)
438 {
439 #ifdef DEBUG
440 int i;
441 #endif
442 int ret = 0;
443 int olderr = errno;
444 FILE *stream;
445
446 if (!data) /* Interpret data as a FILE stream. */
447 return 0; /* If it's NULL, we can't do anything. */
448 stream = (FILE*)data;
449
450 #ifdef DEBUG
451 if (level == NTFS_LOG_LEVEL_LEAVE) {
452 if (tab)
453 tab--;
454 return 0;
455 }
456
457 for (i = 0; i < tab; i++)
458 ret += fprintf(stream, " ");
459 #endif
460 if ((ntfs_log.flags & NTFS_LOG_FLAG_ONLYNAME) &&
461 (strchr(file, PATH_SEP))) /* Abbreviate the filename */
462 file = strrchr(file, PATH_SEP) + 1;
463
464 if (ntfs_log.flags & NTFS_LOG_FLAG_PREFIX) /* Prefix the output */
465 ret += fprintf(stream, "%s", ntfs_log_get_prefix(level));
466
467 if (ntfs_log.flags & NTFS_LOG_FLAG_FILENAME) /* Source filename */
468 ret += fprintf(stream, "%s ", file);
469
470 if (ntfs_log.flags & NTFS_LOG_FLAG_LINE) /* Source line number */
471 ret += fprintf(stream, "(%d) ", line);
472
473 if ((ntfs_log.flags & NTFS_LOG_FLAG_FUNCTION) || /* Source function */
474 (level & NTFS_LOG_LEVEL_TRACE) || (level & NTFS_LOG_LEVEL_ENTER))
475 ret += fprintf(stream, "%s(): ", function);
476
477 ret += vfprintf(stream, format, args);
478
479 if (level & NTFS_LOG_LEVEL_PERROR)
480 ret += fprintf(stream, ": %s\n", strerror(olderr));
481
482 #ifdef DEBUG
483 if (level == NTFS_LOG_LEVEL_ENTER)
484 tab++;
485 #endif
486 fflush(stream);
487 errno = olderr;
488 return ret;
489 }
490
491 /**
492 * ntfs_log_handler_null - Null logging handler (no output)
493 * @function: Function in which the log line occurred
494 * @file: File in which the log line occurred
495 * @line: Line number on which the log line occurred
496 * @level: Level at which the line is logged
497 * @data: User specified data, possibly specific to a handler
498 * @format: printf-style formatting string
499 * @args: Arguments to be formatted
500 *
501 * This handler produces no output. It provides a way to temporarily disable
502 * logging, without having to change the levels and flags.
503 *
504 * Returns: 0 Message wasn't logged
505 */
ntfs_log_handler_null(const char * function,const char * file,int line,u32 level,void * data,const char * format,va_list args)506 int ntfs_log_handler_null(const char *function __attribute__((unused)), const char *file __attribute__((unused)),
507 int line __attribute__((unused)), u32 level __attribute__((unused)), void *data __attribute__((unused)),
508 const char *format __attribute__((unused)), va_list args __attribute__((unused)))
509 {
510 return 0;
511 }
512
513 /**
514 * ntfs_log_handler_stdout - All logs go to stdout
515 * @function: Function in which the log line occurred
516 * @file: File in which the log line occurred
517 * @line: Line number on which the log line occurred
518 * @level: Level at which the line is logged
519 * @data: User specified data, possibly specific to a handler
520 * @format: printf-style formatting string
521 * @args: Arguments to be formatted
522 *
523 * Display a log message to stdout.
524 *
525 * Note: For this handler, @data is a pointer to a FILE output stream.
526 * If @data is NULL, then stdout will be used.
527 *
528 * Note: This function calls ntfs_log_handler_fprintf to do the main work.
529 *
530 * Returns: -1 Error occurred
531 * 0 Message wasn't logged
532 * num Number of output characters
533 */
ntfs_log_handler_stdout(const char * function,const char * file,int line,u32 level,void * data,const char * format,va_list args)534 int ntfs_log_handler_stdout(const char *function, const char *file,
535 int line, u32 level, void *data, const char *format, va_list args)
536 {
537 if (!data)
538 data = stdout;
539
540 return ntfs_log_handler_fprintf(function, file, line, level, data, format, args);
541 }
542
543 /**
544 * ntfs_log_handler_outerr - Logs go to stdout/stderr depending on level
545 * @function: Function in which the log line occurred
546 * @file: File in which the log line occurred
547 * @line: Line number on which the log line occurred
548 * @level: Level at which the line is logged
549 * @data: User specified data, possibly specific to a handler
550 * @format: printf-style formatting string
551 * @args: Arguments to be formatted
552 *
553 * Display a log message. The output stream will be determined by the log
554 * level.
555 *
556 * Note: For this handler, @data is a pointer to a FILE output stream.
557 * If @data is NULL, the function ntfs_log_get_stream will be called
558 *
559 * Note: This function calls ntfs_log_handler_fprintf to do the main work.
560 *
561 * Returns: -1 Error occurred
562 * 0 Message wasn't logged
563 * num Number of output characters
564 */
ntfs_log_handler_outerr(const char * function,const char * file,int line,u32 level,void * data,const char * format,va_list args)565 int ntfs_log_handler_outerr(const char *function, const char *file,
566 int line, u32 level, void *data, const char *format, va_list args)
567 {
568 if (!data)
569 data = ntfs_log_get_stream(level);
570
571 return ntfs_log_handler_fprintf(function, file, line, level, data, format, args);
572 }
573
574 /**
575 * ntfs_log_handler_stderr - All logs go to stderr
576 * @function: Function in which the log line occurred
577 * @file: File in which the log line occurred
578 * @line: Line number on which the log line occurred
579 * @level: Level at which the line is logged
580 * @data: User specified data, possibly specific to a handler
581 * @format: printf-style formatting string
582 * @args: Arguments to be formatted
583 *
584 * Display a log message to stderr.
585 *
586 * Note: For this handler, @data is a pointer to a FILE output stream.
587 * If @data is NULL, then stdout will be used.
588 *
589 * Note: This function calls ntfs_log_handler_fprintf to do the main work.
590 *
591 * Returns: -1 Error occurred
592 * 0 Message wasn't logged
593 * num Number of output characters
594 */
ntfs_log_handler_stderr(const char * function,const char * file,int line,u32 level,void * data,const char * format,va_list args)595 int ntfs_log_handler_stderr(const char *function, const char *file,
596 int line, u32 level, void *data, const char *format, va_list args)
597 {
598 if (!data)
599 data = stderr;
600
601 return ntfs_log_handler_fprintf(function, file, line, level, data, format, args);
602 }
603
604
605 /**
606 * ntfs_log_parse_option - Act upon command line options
607 * @option: Option flag
608 *
609 * Delegate some of the work of parsing the command line. All the options begin
610 * with "--log-". Options cause log levels to be enabled in @ntfs_log (the
611 * global logging structure).
612 *
613 * Note: The "colour" option changes the logging handler.
614 *
615 * Returns: TRUE Option understood
616 * FALSE Invalid log option
617 */
ntfs_log_parse_option(const char * option)618 BOOL ntfs_log_parse_option(const char *option)
619 {
620 if (strcmp(option, "--log-debug") == 0) {
621 ntfs_log_set_levels(NTFS_LOG_LEVEL_DEBUG);
622 return TRUE;
623 } else if (strcmp(option, "--log-verbose") == 0) {
624 ntfs_log_set_levels(NTFS_LOG_LEVEL_VERBOSE);
625 return TRUE;
626 } else if (strcmp(option, "--log-quiet") == 0) {
627 ntfs_log_clear_levels(NTFS_LOG_LEVEL_QUIET);
628 return TRUE;
629 } else if (strcmp(option, "--log-trace") == 0) {
630 ntfs_log_set_levels(NTFS_LOG_LEVEL_TRACE);
631 return TRUE;
632 }
633
634 ntfs_log_debug("Unknown logging option '%s'\n", option);
635 return FALSE;
636 }
637
638