xref: /haiku/src/add-ons/kernel/file_systems/ntfs/libntfs/logging.c (revision 0562493379cd52eb7103531f895f10bb8e77c085)
1 /**
2  * logging.c - Centralised logging.  Originated from the Linux-NTFS project.
3  *
4  * Copyright (c) 2005 Richard Russon
5  * Copyright (c) 2005-2006 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 /* Colour prefixes and a suffix */
54 static const char *col_green  = "\e[32m";
55 static const char *col_cyan   = "\e[36m";
56 static const char *col_yellow = "\e[01;33m";
57 static const char *col_red    = "\e[01;31m";
58 static const char *col_redinv = "\e[01;07;31m";
59 static const char *col_end    = "\e[0m";
60 
61 /**
62  * struct ntfs_logging - Control info for the logging system
63  * @levels:	Bitfield of logging levels
64  * @flags:	Flags which affect the output style
65  * @handler:	Function to perform the actual logging
66  */
67 struct ntfs_logging {
68 	u32 levels;
69 	u32 flags;
70 	ntfs_log_handler *handler;
71 };
72 
73 /**
74  * ntfs_log
75  * This struct controls all the logging within the library and tools.
76  */
77 static struct ntfs_logging ntfs_log = {
78 #ifdef DEBUG
79 	NTFS_LOG_LEVEL_DEBUG | NTFS_LOG_LEVEL_TRACE |
80 #endif
81 	NTFS_LOG_LEVEL_INFO | NTFS_LOG_LEVEL_QUIET | NTFS_LOG_LEVEL_WARNING |
82 	NTFS_LOG_LEVEL_ERROR | NTFS_LOG_LEVEL_PERROR | NTFS_LOG_LEVEL_CRITICAL |
83 	NTFS_LOG_LEVEL_PROGRESS,
84 	NTFS_LOG_FLAG_ONLYNAME,
85 #ifdef DEBUG
86 	ntfs_log_handler_outerr
87 #else
88 	ntfs_log_handler_null
89 #endif
90 };
91 
92 
93 /**
94  * ntfs_log_get_levels - Get a list of the current logging levels
95  *
96  * Find out which logging levels are enabled.
97  *
98  * Returns:  Log levels in a 32-bit field
99  */
100 u32 ntfs_log_get_levels(void)
101 {
102 	return ntfs_log.levels;
103 }
104 
105 /**
106  * ntfs_log_set_levels - Enable extra logging levels
107  * @levels:	32-bit field of log levels to set
108  *
109  * Enable one or more logging levels.
110  * The logging levels are named: NTFS_LOG_LEVEL_*.
111  *
112  * Returns:  Log levels that were enabled before the call
113  */
114 u32 ntfs_log_set_levels(u32 levels)
115 {
116 	u32 old;
117 	old = ntfs_log.levels;
118 	ntfs_log.levels |= levels;
119 	return old;
120 }
121 
122 /**
123  * ntfs_log_clear_levels - Disable some logging levels
124  * @levels:	32-bit field of log levels to clear
125  *
126  * Disable one or more logging levels.
127  * The logging levels are named: NTFS_LOG_LEVEL_*.
128  *
129  * Returns:  Log levels that were enabled before the call
130  */
131 u32 ntfs_log_clear_levels(u32 levels)
132 {
133 	u32 old;
134 	old = ntfs_log.levels;
135 	ntfs_log.levels &= (~levels);
136 	return old;
137 }
138 
139 
140 /**
141  * ntfs_log_get_flags - Get a list of logging style flags
142  *
143  * Find out which logging flags are enabled.
144  *
145  * Returns:  Logging flags in a 32-bit field
146  */
147 u32 ntfs_log_get_flags(void)
148 {
149 	return ntfs_log.flags;
150 }
151 
152 /**
153  * ntfs_log_set_flags - Enable extra logging style flags
154  * @flags:	32-bit field of logging flags to set
155  *
156  * Enable one or more logging flags.
157  * The log flags are named: NTFS_LOG_LEVEL_*.
158  *
159  * Returns:  Logging flags that were enabled before the call
160  */
161 u32 ntfs_log_set_flags(u32 flags)
162 {
163 	u32 old;
164 	old = ntfs_log.flags;
165 	ntfs_log.flags |= flags;
166 	return old;
167 }
168 
169 /**
170  * ntfs_log_clear_flags - Disable some logging styles
171  * @flags:	32-bit field of logging flags to clear
172  *
173  * Disable one or more logging flags.
174  * The log flags are named: NTFS_LOG_LEVEL_*.
175  *
176  * Returns:  Logging flags that were enabled before the call
177  */
178 u32 ntfs_log_clear_flags(u32 flags)
179 {
180 	u32 old;
181 	old = ntfs_log.flags;
182 	ntfs_log.flags &= (~flags);
183 	return old;
184 }
185 
186 
187 /**
188  * ntfs_log_get_stream - Default output streams for logging levels
189  * @level:	Log level
190  *
191  * By default, urgent messages are sent to "stderr".
192  * Other messages are sent to "stdout".
193  *
194  * Returns:  "string"  Prefix to be used
195  */
196 static FILE * ntfs_log_get_stream(u32 level)
197 {
198 #if defined(__BEOS__) || defined(__HAIKU__)
199 	return NULL;
200 #elif
201 	FILE *stream;
202 
203 	switch (level) {
204 		case NTFS_LOG_LEVEL_INFO:
205 		case NTFS_LOG_LEVEL_QUIET:
206 		case NTFS_LOG_LEVEL_PROGRESS:
207 		case NTFS_LOG_LEVEL_VERBOSE:
208 			stream = stdout;
209 			break;
210 
211 		case NTFS_LOG_LEVEL_DEBUG:
212 		case NTFS_LOG_LEVEL_TRACE:
213 		case NTFS_LOG_LEVEL_WARNING:
214 		case NTFS_LOG_LEVEL_ERROR:
215 		case NTFS_LOG_LEVEL_CRITICAL:
216 		case NTFS_LOG_LEVEL_PERROR:
217 		default:
218 			stream = stderr;
219 			break;
220 	}
221 
222 	return stream;
223 #endif
224 }
225 
226 /**
227  * ntfs_log_get_prefix - Default prefixes for logging levels
228  * @level:	Log level to be prefixed
229  *
230  * Prefixing the logging output can make it easier to parse.
231  *
232  * Returns:  "string"  Prefix to be used
233  */
234 static const char * ntfs_log_get_prefix(u32 level)
235 {
236 	const char *prefix;
237 
238 	switch (level) {
239 		case NTFS_LOG_LEVEL_DEBUG:
240 			prefix = "DEBUG: ";
241 			break;
242 		case NTFS_LOG_LEVEL_TRACE:
243 			prefix = "TRACE: ";
244 			break;
245 		case NTFS_LOG_LEVEL_QUIET:
246 			prefix = "QUIET: ";
247 			break;
248 		case NTFS_LOG_LEVEL_INFO:
249 			prefix = "INFO: ";
250 			break;
251 		case NTFS_LOG_LEVEL_VERBOSE:
252 			prefix = "VERBOSE: ";
253 			break;
254 		case NTFS_LOG_LEVEL_PROGRESS:
255 			prefix = "PROGRESS: ";
256 			break;
257 		case NTFS_LOG_LEVEL_WARNING:
258 			prefix = "WARNING: ";
259 			break;
260 		case NTFS_LOG_LEVEL_ERROR:
261 			prefix = "ERROR: ";
262 			break;
263 		case NTFS_LOG_LEVEL_PERROR:
264 			prefix = "ERROR: ";
265 			break;
266 		case NTFS_LOG_LEVEL_CRITICAL:
267 			prefix = "CRITICAL: ";
268 			break;
269 		default:
270 			prefix = "";
271 			break;
272 	}
273 
274 	return prefix;
275 }
276 
277 
278 /**
279  * ntfs_log_set_handler - Provide an alternate logging handler
280  * @handler:	function to perform the logging
281  *
282  * This alternate handler will be called for all future logging requests.
283  * If no @handler is specified, logging will revert to the default handler.
284  */
285 void ntfs_log_set_handler(ntfs_log_handler *handler)
286 {
287 	if (handler) {
288 		ntfs_log.handler = handler;
289 #ifdef HAVE_SYSLOG_H
290 		if (handler == ntfs_log_handler_syslog)
291 			openlog("libntfs", LOG_PID, LOG_USER);
292 #endif
293 	} else
294 		ntfs_log.handler = ntfs_log_handler_null;
295 }
296 
297 /**
298  * ntfs_log_redirect - Pass on the request to the real handler
299  * @function:	Function in which the log line occurred
300  * @file:	File in which the log line occurred
301  * @line:	Line number on which the log line occurred
302  * @level:	Level at which the line is logged
303  * @data:	User specified data, possibly specific to a handler
304  * @format:	printf-style formatting string
305  * @...:	Arguments to be formatted
306  *
307  * This is just a redirector function.  The arguments are simply passed to the
308  * main logging handler (as defined in the global logging struct @ntfs_log).
309  *
310  * Returns:  -1  Error occurred
311  *            0  Message wasn't logged
312  *          num  Number of output characters
313  */
314 int ntfs_log_redirect(const char *function, const char *file,
315 	int line, u32 level, void *data, const char *format, ...)
316 {
317 	int olderr = errno;
318 	int ret;
319 	va_list args;
320 
321 	if (!(ntfs_log.levels & level))		/* Don't log this message */
322 		return 0;
323 
324 	va_start(args, format);
325 	errno = olderr;
326 	ret = ntfs_log.handler(function, file, line, level, data, format, args);
327 	va_end(args);
328 
329 	errno = olderr;
330 	return ret;
331 }
332 
333 
334 /**
335  * ntfs_log_handler_syslog - syslog logging handler
336  * @function:	Function in which the log line occurred
337  * @file:	File in which the log line occurred
338  * @line:	Line number on which the log line occurred
339  * @level:	Level at which the line is logged
340  * @data:	User specified data, possibly specific to a handler
341  * @format:	printf-style formatting string
342  * @args:	Arguments to be formatted
343  *
344  * A simple syslog logging handler.  Ignores colors.
345  *
346  * Returns:  -1  Error occurred
347  *            0  Message wasn't logged
348  *          num  Number of output characters
349  */
350 
351 
352 #ifdef HAVE_SYSLOG_H
353 
354 #define LOG_LINE_LEN 	512
355 
356 int ntfs_log_handler_syslog(const char *function  __attribute__((unused)),
357 			    const char *file __attribute__((unused)),
358 			    int line __attribute__((unused)), u32 level,
359 			    void *data __attribute__((unused)),
360 			    const char *format, va_list args)
361 {
362 	char log[LOG_LINE_LEN];
363 	int ret, olderr = errno;
364 
365 	ret = vsnprintf(log, LOG_LINE_LEN, format, args);
366 	if (ret < 0) {
367 		vsyslog(LOG_NOTICE, format, args);
368 		return 1;
369 	}
370 
371 	if ((LOG_LINE_LEN > ret + 3) && (level & NTFS_LOG_LEVEL_PERROR)) {
372 		strncat(log, ": ", LOG_LINE_LEN - ret - 1);
373 		strncat(log, strerror(olderr), LOG_LINE_LEN - (ret + 3));
374 		ret = strlen(log);
375 	}
376 
377 	syslog(LOG_NOTICE, "%s", log);
378 
379 	errno = olderr;
380 	return ret;
381 }
382 #endif
383 
384 /**
385  * ntfs_log_handler_fprintf - Basic logging handler
386  * @function:	Function in which the log line occurred
387  * @file:	File in which the log line occurred
388  * @line:	Line number on which the log line occurred
389  * @level:	Level at which the line is logged
390  * @data:	User specified data, possibly specific to a handler
391  * @format:	printf-style formatting string
392  * @args:	Arguments to be formatted
393  *
394  * A simple logging handler.  This is where the log line is finally displayed.
395  * It is more likely that you will want to set the handler to either
396  * ntfs_log_handler_outerr or ntfs_log_handler_stderr.
397  *
398  * Note: For this handler, @data is a pointer to a FILE output stream.
399  *       If @data is NULL, nothing will be displayed.
400  *
401  * Returns:  -1  Error occurred
402  *            0  Message wasn't logged
403  *          num  Number of output characters
404  */
405 int ntfs_log_handler_fprintf(const char *function, const char *file,
406 	int line, u32 level, void *data, const char *format, va_list args)
407 {
408 #if defined(__BEOS__) || defined(__HAIKU__)
409 	return 0;
410 #elif
411 	int ret = 0;
412 	int olderr = errno;
413 	FILE *stream;
414 	const char *col_prefix = NULL;
415 	const char *col_suffix = NULL;
416 
417 	if (!data)		/* Interpret data as a FILE stream. */
418 		return 0;	/* If it's NULL, we can't do anything. */
419 	stream = (FILE*)data;
420 
421 	if (ntfs_log.flags & NTFS_LOG_FLAG_COLOUR) {
422 		/* Pick a colour determined by the log level */
423 		switch (level) {
424 			case NTFS_LOG_LEVEL_DEBUG:
425 				col_prefix = col_green;
426 				col_suffix = col_end;
427 				break;
428 			case NTFS_LOG_LEVEL_TRACE:
429 				col_prefix = col_cyan;
430 				col_suffix = col_end;
431 				break;
432 			case NTFS_LOG_LEVEL_WARNING:
433 				col_prefix = col_yellow;
434 				col_suffix = col_end;
435 				break;
436 			case NTFS_LOG_LEVEL_ERROR:
437 			case NTFS_LOG_LEVEL_PERROR:
438 				col_prefix = col_red;
439 				col_suffix = col_end;
440 				break;
441 			case NTFS_LOG_LEVEL_CRITICAL:
442 				col_prefix = col_redinv;
443 				col_suffix = col_end;
444 				break;
445 		}
446 	}
447 
448 	if (col_prefix)
449 		ret += fprintf(stream, col_prefix);
450 
451 	if ((ntfs_log.flags & NTFS_LOG_FLAG_ONLYNAME) &&
452 	    (strchr(file, PATH_SEP)))		/* Abbreviate the filename */
453 		file = strrchr(file, PATH_SEP) + 1;
454 
455 	if (ntfs_log.flags & NTFS_LOG_FLAG_PREFIX)	/* Prefix the output */
456 		ret += fprintf(stream, "%s", ntfs_log_get_prefix(level));
457 
458 	if (ntfs_log.flags & NTFS_LOG_FLAG_FILENAME)	/* Source filename */
459 		ret += fprintf(stream, "%s ", file);
460 
461 	if (ntfs_log.flags & NTFS_LOG_FLAG_LINE)	/* Source line number */
462 		ret += fprintf(stream, "(%d) ", line);
463 
464 	if ((ntfs_log.flags & NTFS_LOG_FLAG_FUNCTION) || /* Source function */
465 	    (level & NTFS_LOG_LEVEL_TRACE))
466 		ret += fprintf(stream, "%s(): ", function);
467 
468 	ret += vfprintf(stream, format, args);
469 
470 	if (level & NTFS_LOG_LEVEL_PERROR)
471 		ret += fprintf(stream, ": %s\n", strerror(olderr));
472 
473 	if (col_suffix)
474 		ret += fprintf(stream, col_suffix);
475 
476 
477 	fflush(stream);
478 	errno = olderr;
479 	return ret;
480 #endif
481 }
482 
483 /**
484  * ntfs_log_handler_null - Null logging handler (no output)
485  * @function:	Function in which the log line occurred
486  * @file:	File in which the log line occurred
487  * @line:	Line number on which the log line occurred
488  * @level:	Level at which the line is logged
489  * @data:	User specified data, possibly specific to a handler
490  * @format:	printf-style formatting string
491  * @args:	Arguments to be formatted
492  *
493  * This handler produces no output.  It provides a way to temporarily disable
494  * logging, without having to change the levels and flags.
495  *
496  * Returns:  0  Message wasn't logged
497  */
498 int ntfs_log_handler_null(const char *function __attribute__((unused)), const char *file __attribute__((unused)),
499 	int line __attribute__((unused)), u32 level __attribute__((unused)), void *data __attribute__((unused)),
500 	const char *format __attribute__((unused)), va_list args __attribute__((unused)))
501 {
502 	return 0;
503 }
504 
505 /**
506  * ntfs_log_handler_stdout - All logs go to stdout
507  * @function:	Function in which the log line occurred
508  * @file:	File in which the log line occurred
509  * @line:	Line number on which the log line occurred
510  * @level:	Level at which the line is logged
511  * @data:	User specified data, possibly specific to a handler
512  * @format:	printf-style formatting string
513  * @args:	Arguments to be formatted
514  *
515  * Display a log message to stdout.
516  *
517  * Note: For this handler, @data is a pointer to a FILE output stream.
518  *       If @data is NULL, then stdout will be used.
519  *
520  * Note: This function calls ntfs_log_handler_fprintf to do the main work.
521  *
522  * Returns:  -1  Error occurred
523  *            0  Message wasn't logged
524  *          num  Number of output characters
525  */
526 int ntfs_log_handler_stdout(const char *function, const char *file,
527 	int line, u32 level, void *data, const char *format, va_list args)
528 {
529 #if defined(__BEOS__) || defined(__HAIKU__)
530 	return 0;
531 #elif
532 	if (!data)
533 		data = stdout;
534 
535 	return ntfs_log_handler_fprintf(function, file, line, level, data, format, args);
536 #endif
537 }
538 
539 /**
540  * ntfs_log_handler_outerr - Logs go to stdout/stderr depending on level
541  * @function:	Function in which the log line occurred
542  * @file:	File in which the log line occurred
543  * @line:	Line number on which the log line occurred
544  * @level:	Level at which the line is logged
545  * @data:	User specified data, possibly specific to a handler
546  * @format:	printf-style formatting string
547  * @args:	Arguments to be formatted
548  *
549  * Display a log message.  The output stream will be determined by the log
550  * level.
551  *
552  * Note: For this handler, @data is a pointer to a FILE output stream.
553  *       If @data is NULL, the function ntfs_log_get_stream will be called
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_outerr(const char *function, const char *file,
562 	int line, u32 level, void *data, const char *format, va_list args)
563 {
564 #if defined(__BEOS__) || defined(__HAIKU__)
565 	return 0;
566 #elif
567 	if (!data)
568 		data = ntfs_log_get_stream(level);
569 
570 	return ntfs_log_handler_fprintf(function, file, line, level, data, format, args);
571 #endif
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  */
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 defined(__BEOS__) || defined(__HAIKU__)
599 	return 0;
600 #elif
601 	if (!data)
602 		data = stderr;
603 
604 	return ntfs_log_handler_fprintf(function, file, line, level, data, format, args);
605 #endif
606 }
607 
608 
609 /**
610  * ntfs_log_parse_option - Act upon command line options
611  * @option:	Option flag
612  *
613  * Delegate some of the work of parsing the command line.  All the options begin
614  * with "--log-".  Options cause log levels to be enabled in @ntfs_log (the
615  * global logging structure).
616  *
617  * Note: The "colour" option changes the logging handler.
618  *
619  * Returns:  TRUE  Option understood
620  *          FALSE  Invalid log option
621  */
622 BOOL ntfs_log_parse_option(const char *option)
623 {
624 	if (strcmp(option, "--log-debug") == 0) {
625 		ntfs_log_set_levels(NTFS_LOG_LEVEL_DEBUG);
626 		return TRUE;
627 	} else if (strcmp(option, "--log-verbose") == 0) {
628 		ntfs_log_set_levels(NTFS_LOG_LEVEL_VERBOSE);
629 		return TRUE;
630 	} else if (strcmp(option, "--log-quiet") == 0) {
631 		ntfs_log_clear_levels(NTFS_LOG_LEVEL_QUIET);
632 		return TRUE;
633 	} else if (strcmp(option, "--log-trace") == 0) {
634 		ntfs_log_set_levels(NTFS_LOG_LEVEL_TRACE);
635 		return TRUE;
636 	} else if ((strcmp(option, "--log-colour") == 0) ||
637 		   (strcmp(option, "--log-color") == 0)) {
638 		ntfs_log_set_flags(NTFS_LOG_FLAG_COLOUR);
639 		return TRUE;
640 	}
641 
642 	ntfs_log_debug("Unknown logging option '%s'\n", option);
643 	return FALSE;
644 }
645 
646