xref: /haiku/src/add-ons/kernel/file_systems/ntfs/libntfs/logging.c (revision fc7456e9b1ec38c941134ed6d01c438cf289381e)
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  */
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  */
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  */
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  */
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  */
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  */
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__
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  */
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  */
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  */
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 
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 
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  */
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  */
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  */
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  */
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  */
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  */
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