xref: /haiku/src/add-ons/kernel/file_systems/ntfs/libntfs/logging.c (revision 323b65468e5836bb27a5e373b14027d902349437)
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  *
7  * This program/include file is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License as published
9  * by the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program/include file is distributed in the hope that it will be
13  * useful, but WITHOUT ANY WARRANTY; without even the implied warranty
14  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program (in the main directory of the NTFS-3G
19  * distribution in the file COPYING); if not, write to the Free Software
20  * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  */
22 
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
26 
27 #ifdef HAVE_STDIO_H
28 #include <stdio.h>
29 #endif
30 #ifdef HAVE_ERRNO_H
31 #include <errno.h>
32 #endif
33 #ifdef HAVE_STDARG_H
34 #include <stdarg.h>
35 #endif
36 #ifdef HAVE_STRING_H
37 #include <string.h>
38 #endif
39 #ifdef HAVE_STDLIB_H
40 #include <stdlib.h>
41 #endif
42 #ifdef HAVE_SYSLOG_H
43 #include <syslog.h>
44 #endif
45 
46 #include "logging.h"
47 #include "misc.h"
48 
49 #ifndef PATH_SEP
50 #define PATH_SEP '/'
51 #endif
52 
53 #ifdef DEBUG
54 static int tab;
55 #endif
56 
57 /* Some gcc 3.x, 4.[01].X crash with internal compiler error. */
58 #if __GNUC__ <= 3 || (__GNUC__ == 4 && __GNUC_MINOR__ <= 1)
59 # define  BROKEN_GCC_FORMAT_ATTRIBUTE
60 #else
61 # define  BROKEN_GCC_FORMAT_ATTRIBUTE __attribute__((format(printf, 6, 0)))
62 #endif
63 
64 /**
65  * struct ntfs_logging - Control info for the logging system
66  * @levels:	Bitfield of logging levels
67  * @flags:	Flags which affect the output style
68  * @handler:	Function to perform the actual logging
69  */
70 struct ntfs_logging {
71 	u32 levels;
72 	u32 flags;
73 	ntfs_log_handler *handler BROKEN_GCC_FORMAT_ATTRIBUTE;
74 };
75 
76 /**
77  * ntfs_log
78  * This struct controls all the logging within the library and tools.
79  */
80 static struct ntfs_logging ntfs_log = {
81 #ifdef DEBUG
82 	NTFS_LOG_LEVEL_DEBUG | NTFS_LOG_LEVEL_TRACE | NTFS_LOG_LEVEL_ENTER |
83 	NTFS_LOG_LEVEL_LEAVE |
84 #endif
85 	NTFS_LOG_LEVEL_INFO | NTFS_LOG_LEVEL_QUIET | NTFS_LOG_LEVEL_WARNING |
86 	NTFS_LOG_LEVEL_ERROR | NTFS_LOG_LEVEL_PERROR | NTFS_LOG_LEVEL_CRITICAL |
87 	NTFS_LOG_LEVEL_PROGRESS,
88 	NTFS_LOG_FLAG_ONLYNAME,
89 #ifdef DEBUG
90 	ntfs_log_handler_outerr
91 #else
92 	ntfs_log_handler_null
93 #endif
94 };
95 
96 
97 /**
98  * ntfs_log_get_levels - Get a list of the current logging levels
99  *
100  * Find out which logging levels are enabled.
101  *
102  * Returns:  Log levels in a 32-bit field
103  */
104 u32 ntfs_log_get_levels(void)
105 {
106 	return ntfs_log.levels;
107 }
108 
109 /**
110  * ntfs_log_set_levels - Enable extra logging levels
111  * @levels:	32-bit field of log levels to set
112  *
113  * Enable one or more logging levels.
114  * The logging levels are named: NTFS_LOG_LEVEL_*.
115  *
116  * Returns:  Log levels that were enabled before the call
117  */
118 u32 ntfs_log_set_levels(u32 levels)
119 {
120 	u32 old;
121 	old = ntfs_log.levels;
122 	ntfs_log.levels |= levels;
123 	return old;
124 }
125 
126 /**
127  * ntfs_log_clear_levels - Disable some logging levels
128  * @levels:	32-bit field of log levels to clear
129  *
130  * Disable one or more logging levels.
131  * The logging levels are named: NTFS_LOG_LEVEL_*.
132  *
133  * Returns:  Log levels that were enabled before the call
134  */
135 u32 ntfs_log_clear_levels(u32 levels)
136 {
137 	u32 old;
138 	old = ntfs_log.levels;
139 	ntfs_log.levels &= (~levels);
140 	return old;
141 }
142 
143 
144 /**
145  * ntfs_log_get_flags - Get a list of logging style flags
146  *
147  * Find out which logging flags are enabled.
148  *
149  * Returns:  Logging flags in a 32-bit field
150  */
151 u32 ntfs_log_get_flags(void)
152 {
153 	return ntfs_log.flags;
154 }
155 
156 /**
157  * ntfs_log_set_flags - Enable extra logging style flags
158  * @flags:	32-bit field of logging flags to set
159  *
160  * Enable one or more logging flags.
161  * The log flags are named: NTFS_LOG_LEVEL_*.
162  *
163  * Returns:  Logging flags that were enabled before the call
164  */
165 u32 ntfs_log_set_flags(u32 flags)
166 {
167 	u32 old;
168 	old = ntfs_log.flags;
169 	ntfs_log.flags |= flags;
170 	return old;
171 }
172 
173 /**
174  * ntfs_log_clear_flags - Disable some logging styles
175  * @flags:	32-bit field of logging flags to clear
176  *
177  * Disable one or more logging flags.
178  * The log flags are named: NTFS_LOG_LEVEL_*.
179  *
180  * Returns:  Logging flags that were enabled before the call
181  */
182 u32 ntfs_log_clear_flags(u32 flags)
183 {
184 	u32 old;
185 	old = ntfs_log.flags;
186 	ntfs_log.flags &= (~flags);
187 	return old;
188 }
189 
190 #ifndef __HAIKU__
191 /**
192  * ntfs_log_get_stream - Default output streams for logging levels
193  * @level:	Log level
194  *
195  * By default, urgent messages are sent to "stderr".
196  * Other messages are sent to "stdout".
197  *
198  * Returns:  "string"  Prefix to be used
199  */
200 static FILE * ntfs_log_get_stream(u32 level)
201 {
202 	FILE *stream;
203 
204 	switch (level) {
205 		case NTFS_LOG_LEVEL_INFO:
206 		case NTFS_LOG_LEVEL_QUIET:
207 		case NTFS_LOG_LEVEL_PROGRESS:
208 		case NTFS_LOG_LEVEL_VERBOSE:
209 			stream = stdout;
210 			break;
211 
212 		case NTFS_LOG_LEVEL_DEBUG:
213 		case NTFS_LOG_LEVEL_TRACE:
214 		case NTFS_LOG_LEVEL_ENTER:
215 		case NTFS_LOG_LEVEL_LEAVE:
216 		case NTFS_LOG_LEVEL_WARNING:
217 		case NTFS_LOG_LEVEL_ERROR:
218 		case NTFS_LOG_LEVEL_CRITICAL:
219 		case NTFS_LOG_LEVEL_PERROR:
220 		default:
221 			stream = stderr;
222 			break;
223 	}
224 
225 	return stream;
226 }
227 
228 /**
229  * ntfs_log_get_prefix - Default prefixes for logging levels
230  * @level:	Log level to be prefixed
231  *
232  * Prefixing the logging output can make it easier to parse.
233  *
234  * Returns:  "string"  Prefix to be used
235  */
236 static const char * ntfs_log_get_prefix(u32 level)
237 {
238 	const char *prefix;
239 
240 	switch (level) {
241 		case NTFS_LOG_LEVEL_DEBUG:
242 			prefix = "DEBUG: ";
243 			break;
244 		case NTFS_LOG_LEVEL_TRACE:
245 			prefix = "TRACE: ";
246 			break;
247 		case NTFS_LOG_LEVEL_QUIET:
248 			prefix = "QUIET: ";
249 			break;
250 		case NTFS_LOG_LEVEL_INFO:
251 			prefix = "INFO: ";
252 			break;
253 		case NTFS_LOG_LEVEL_VERBOSE:
254 			prefix = "VERBOSE: ";
255 			break;
256 		case NTFS_LOG_LEVEL_PROGRESS:
257 			prefix = "PROGRESS: ";
258 			break;
259 		case NTFS_LOG_LEVEL_WARNING:
260 			prefix = "WARNING: ";
261 			break;
262 		case NTFS_LOG_LEVEL_ERROR:
263 			prefix = "ERROR: ";
264 			break;
265 		case NTFS_LOG_LEVEL_PERROR:
266 			prefix = "ERROR: ";
267 			break;
268 		case NTFS_LOG_LEVEL_CRITICAL:
269 			prefix = "CRITICAL: ";
270 			break;
271 		default:
272 			prefix = "";
273 			break;
274 	}
275 
276 	return prefix;
277 }
278 #endif
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  */
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 /**
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  */
318 int 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 
360 int 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);
387 out:
388 	errno = olderr;
389 	return ret;
390 }
391 #endif
392 
393 #ifndef __HAIKU__
394 /**
395  * ntfs_log_handler_fprintf - Basic logging handler
396  * @function:	Function in which the log line occurred
397  * @file:	File in which the log line occurred
398  * @line:	Line number on which the log line occurred
399  * @level:	Level at which the line is logged
400  * @data:	User specified data, possibly specific to a handler
401  * @format:	printf-style formatting string
402  * @args:	Arguments to be formatted
403  *
404  * A simple logging handler.  This is where the log line is finally displayed.
405  * It is more likely that you will want to set the handler to either
406  * ntfs_log_handler_outerr or ntfs_log_handler_stderr.
407  *
408  * Note: For this handler, @data is a pointer to a FILE output stream.
409  *       If @data is NULL, nothing will be displayed.
410  *
411  * Returns:  -1  Error occurred
412  *            0  Message wasn't logged
413  *          num  Number of output characters
414  */
415 int ntfs_log_handler_fprintf(const char *function, const char *file,
416 	int line, u32 level, void *data, const char *format, va_list args)
417 {
418 #ifdef DEBUG
419 	int i;
420 #endif
421 	int ret = 0;
422 	int olderr = errno;
423 	FILE *stream;
424 
425 	if (!data)		/* Interpret data as a FILE stream. */
426 		return 0;	/* If it's NULL, we can't do anything. */
427 	stream = (FILE*)data;
428 
429 #ifdef DEBUG
430 	if (level == NTFS_LOG_LEVEL_LEAVE) {
431 		if (tab)
432 			tab--;
433 		return 0;
434 	}
435 
436 	for (i = 0; i < tab; i++)
437 		ret += fprintf(stream, " ");
438 #endif
439 	if ((ntfs_log.flags & NTFS_LOG_FLAG_ONLYNAME) &&
440 	    (strchr(file, PATH_SEP)))		/* Abbreviate the filename */
441 		file = strrchr(file, PATH_SEP) + 1;
442 
443 	if (ntfs_log.flags & NTFS_LOG_FLAG_PREFIX)	/* Prefix the output */
444 		ret += fprintf(stream, "%s", ntfs_log_get_prefix(level));
445 
446 	if (ntfs_log.flags & NTFS_LOG_FLAG_FILENAME)	/* Source filename */
447 		ret += fprintf(stream, "%s ", file);
448 
449 	if (ntfs_log.flags & NTFS_LOG_FLAG_LINE)	/* Source line number */
450 		ret += fprintf(stream, "(%d) ", line);
451 
452 	if ((ntfs_log.flags & NTFS_LOG_FLAG_FUNCTION) || /* Source function */
453 	    (level & NTFS_LOG_LEVEL_TRACE) || (level & NTFS_LOG_LEVEL_ENTER))
454 		ret += fprintf(stream, "%s(): ", function);
455 
456 	ret += vfprintf(stream, format, args);
457 
458 	if (level & NTFS_LOG_LEVEL_PERROR)
459 		ret += fprintf(stream, ": %s\n", strerror(olderr));
460 
461 #ifdef DEBUG
462 	if (level == NTFS_LOG_LEVEL_ENTER)
463 		tab++;
464 #endif
465 	fflush(stream);
466 	errno = olderr;
467 	return ret;
468 }
469 
470 #endif // __HAIKU__
471 
472 /**
473  * ntfs_log_handler_null - Null logging handler (no output)
474  * @function:	Function in which the log line occurred
475  * @file:	File in which the log line occurred
476  * @line:	Line number on which the log line occurred
477  * @level:	Level at which the line is logged
478  * @data:	User specified data, possibly specific to a handler
479  * @format:	printf-style formatting string
480  * @args:	Arguments to be formatted
481  *
482  * This handler produces no output.  It provides a way to temporarily disable
483  * logging, without having to change the levels and flags.
484  *
485  * Returns:  0  Message wasn't logged
486  */
487 int ntfs_log_handler_null(const char *function __attribute__((unused)), const char *file __attribute__((unused)),
488 	int line __attribute__((unused)), u32 level __attribute__((unused)), void *data __attribute__((unused)),
489 	const char *format __attribute__((unused)), va_list args __attribute__((unused)))
490 {
491 	return 0;
492 }
493 
494 #ifndef __HAIKU__
495 
496 /**
497  * ntfs_log_handler_stdout - All logs go to stdout
498  * @function:	Function in which the log line occurred
499  * @file:	File in which the log line occurred
500  * @line:	Line number on which the log line occurred
501  * @level:	Level at which the line is logged
502  * @data:	User specified data, possibly specific to a handler
503  * @format:	printf-style formatting string
504  * @args:	Arguments to be formatted
505  *
506  * Display a log message to stdout.
507  *
508  * Note: For this handler, @data is a pointer to a FILE output stream.
509  *       If @data is NULL, then stdout will be used.
510  *
511  * Note: This function calls ntfs_log_handler_fprintf to do the main work.
512  *
513  * Returns:  -1  Error occurred
514  *            0  Message wasn't logged
515  *          num  Number of output characters
516  */
517 int ntfs_log_handler_stdout(const char *function, const char *file,
518 	int line, u32 level, void *data, const char *format, va_list args)
519 {
520 	if (!data)
521 		data = stdout;
522 
523 	return ntfs_log_handler_fprintf(function, file, line, level, data, format, args);
524 }
525 
526 /**
527  * ntfs_log_handler_outerr - Logs go to stdout/stderr depending on level
528  * @function:	Function in which the log line occurred
529  * @file:	File in which the log line occurred
530  * @line:	Line number on which the log line occurred
531  * @level:	Level at which the line is logged
532  * @data:	User specified data, possibly specific to a handler
533  * @format:	printf-style formatting string
534  * @args:	Arguments to be formatted
535  *
536  * Display a log message.  The output stream will be determined by the log
537  * level.
538  *
539  * Note: For this handler, @data is a pointer to a FILE output stream.
540  *       If @data is NULL, the function ntfs_log_get_stream will be called
541  *
542  * Note: This function calls ntfs_log_handler_fprintf to do the main work.
543  *
544  * Returns:  -1  Error occurred
545  *            0  Message wasn't logged
546  *          num  Number of output characters
547  */
548 int ntfs_log_handler_outerr(const char *function, const char *file,
549 	int line, u32 level, void *data, const char *format, va_list args)
550 {
551 	if (!data)
552 		data = ntfs_log_get_stream(level);
553 
554 	return ntfs_log_handler_fprintf(function, file, line, level, data, format, args);
555 }
556 
557 /**
558  * ntfs_log_handler_stderr - All logs go to stderr
559  * @function:	Function in which the log line occurred
560  * @file:	File in which the log line occurred
561  * @line:	Line number on which the log line occurred
562  * @level:	Level at which the line is logged
563  * @data:	User specified data, possibly specific to a handler
564  * @format:	printf-style formatting string
565  * @args:	Arguments to be formatted
566  *
567  * Display a log message to stderr.
568  *
569  * Note: For this handler, @data is a pointer to a FILE output stream.
570  *       If @data is NULL, then stdout will be used.
571  *
572  * Note: This function calls ntfs_log_handler_fprintf to do the main work.
573  *
574  * Returns:  -1  Error occurred
575  *            0  Message wasn't logged
576  *          num  Number of output characters
577  */
578 int ntfs_log_handler_stderr(const char *function, const char *file,
579 	int line, u32 level, void *data, const char *format, va_list args)
580 {
581 	if (!data)
582 		data = stderr;
583 
584 	return ntfs_log_handler_fprintf(function, file, line, level, data, format, args);
585 }
586 
587 #endif // __HAIKU__
588 
589 
590 /**
591  * ntfs_log_parse_option - Act upon command line options
592  * @option:	Option flag
593  *
594  * Delegate some of the work of parsing the command line.  All the options begin
595  * with "--log-".  Options cause log levels to be enabled in @ntfs_log (the
596  * global logging structure).
597  *
598  * Note: The "colour" option changes the logging handler.
599  *
600  * Returns:  TRUE  Option understood
601  *          FALSE  Invalid log option
602  */
603 BOOL ntfs_log_parse_option(const char *option)
604 {
605 	if (strcmp(option, "--log-debug") == 0) {
606 		ntfs_log_set_levels(NTFS_LOG_LEVEL_DEBUG);
607 		return TRUE;
608 	} else if (strcmp(option, "--log-verbose") == 0) {
609 		ntfs_log_set_levels(NTFS_LOG_LEVEL_VERBOSE);
610 		return TRUE;
611 	} else if (strcmp(option, "--log-quiet") == 0) {
612 		ntfs_log_clear_levels(NTFS_LOG_LEVEL_QUIET);
613 		return TRUE;
614 	} else if (strcmp(option, "--log-trace") == 0) {
615 		ntfs_log_set_levels(NTFS_LOG_LEVEL_TRACE);
616 		return TRUE;
617 	}
618 
619 	ntfs_log_debug("Unknown logging option '%s'\n", option);
620 	return FALSE;
621 }
622 
623