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