• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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