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