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
54static 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 */
70struct 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 */
80static 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 */
104u32 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 */
118u32 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 */
135u32 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 */
151u32 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 */
165u32 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 */
182u32 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
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 */
200static 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 */
236static 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
279
280/**
281 * ntfs_log_set_handler - Provide an alternate logging handler
282 * @handler:	function to perform the logging
283 *
284 * This alternate handler will be called for all future logging requests.
285 * If no @handler is specified, logging will revert to the default handler.
286 */
287void ntfs_log_set_handler(ntfs_log_handler *handler)
288{
289	if (handler) {
290		ntfs_log.handler = handler;
291#ifdef HAVE_SYSLOG_H
292		if (handler == ntfs_log_handler_syslog)
293			openlog("ntfs-3g", LOG_PID, LOG_USER);
294#endif
295	} else
296		ntfs_log.handler = ntfs_log_handler_null;
297}
298
299/**
300 * ntfs_log_redirect - Pass on the request to the real handler
301 * @function:	Function in which the log line occurred
302 * @file:	File in which the log line occurred
303 * @line:	Line number on which the log line occurred
304 * @level:	Level at which the line is logged
305 * @data:	User specified data, possibly specific to a handler
306 * @format:	printf-style formatting string
307 * @...:	Arguments to be formatted
308 *
309 * This is just a redirector function.  The arguments are simply passed to the
310 * main logging handler (as defined in the global logging struct @ntfs_log).
311 *
312 * Returns:  -1  Error occurred
313 *            0  Message wasn't logged
314 *          num  Number of output characters
315 */
316int ntfs_log_redirect(const char *function, const char *file,
317	int line, u32 level, void *data, const char *format, ...)
318{
319	int olderr = errno;
320	int ret;
321	va_list args;
322
323	if (!(ntfs_log.levels & level))		/* Don't log this message */
324		return 0;
325
326	va_start(args, format);
327	errno = olderr;
328	ret = ntfs_log.handler(function, file, line, level, data, format, args);
329	va_end(args);
330
331	errno = olderr;
332	return ret;
333}
334
335
336/**
337 * ntfs_log_handler_syslog - syslog logging handler
338 * @function:	Function in which the log line occurred
339 * @file:	File in which the log line occurred
340 * @line:	Line number on which the log line occurred
341 * @level:	Level at which the line is logged
342 * @data:	User specified data, possibly specific to a handler
343 * @format:	printf-style formatting string
344 * @args:	Arguments to be formatted
345 *
346 * A simple syslog logging handler.  Ignores colors.
347 *
348 * Returns:  -1  Error occurred
349 *            0  Message wasn't logged
350 *          num  Number of output characters
351 */
352
353
354#ifdef HAVE_SYSLOG_H
355
356#define LOG_LINE_LEN 	512
357
358int ntfs_log_handler_syslog(const char *function  __attribute__((unused)),
359			    const char *file __attribute__((unused)),
360			    int line __attribute__((unused)), u32 level,
361			    void *data __attribute__((unused)),
362			    const char *format, va_list args)
363{
364	char logbuf[LOG_LINE_LEN];
365	int ret, olderr = errno;
366
367#ifndef DEBUG
368	if ((level & NTFS_LOG_LEVEL_PERROR) && errno == ENOSPC)
369		return 1;
370#endif
371	ret = vsnprintf(logbuf, LOG_LINE_LEN, format, args);
372	if (ret < 0) {
373		vsyslog(LOG_NOTICE, format, args);
374		ret = 1;
375		goto out;
376	}
377
378	if ((LOG_LINE_LEN > ret + 3) && (level & NTFS_LOG_LEVEL_PERROR)) {
379		strncat(logbuf, ": ", LOG_LINE_LEN - ret - 1);
380		strncat(logbuf, strerror(olderr), LOG_LINE_LEN - (ret + 3));
381		ret = strlen(logbuf);
382	}
383
384	syslog(LOG_NOTICE, "%s", logbuf);
385out:
386	errno = olderr;
387	return ret;
388}
389#endif
390
391/**
392 * ntfs_log_handler_fprintf - Basic logging handler
393 * @function:	Function in which the log line occurred
394 * @file:	File in which the log line occurred
395 * @line:	Line number on which the log line occurred
396 * @level:	Level at which the line is logged
397 * @data:	User specified data, possibly specific to a handler
398 * @format:	printf-style formatting string
399 * @args:	Arguments to be formatted
400 *
401 * A simple logging handler.  This is where the log line is finally displayed.
402 * It is more likely that you will want to set the handler to either
403 * ntfs_log_handler_outerr or ntfs_log_handler_stderr.
404 *
405 * Note: For this handler, @data is a pointer to a FILE output stream.
406 *       If @data is NULL, nothing will be displayed.
407 *
408 * Returns:  -1  Error occurred
409 *            0  Message wasn't logged
410 *          num  Number of output characters
411 */
412int ntfs_log_handler_fprintf(const char *function, const char *file,
413	int line, u32 level, void *data, const char *format, va_list args)
414{
415#ifdef DEBUG
416	int i;
417#endif
418	int ret = 0;
419	int olderr = errno;
420	FILE *stream;
421
422	if (!data)		/* Interpret data as a FILE stream. */
423		return 0;	/* If it's NULL, we can't do anything. */
424	stream = (FILE*)data;
425
426#ifdef DEBUG
427	if (level == NTFS_LOG_LEVEL_LEAVE) {
428		if (tab)
429			tab--;
430		return 0;
431	}
432
433	for (i = 0; i < tab; i++)
434		ret += fprintf(stream, " ");
435#endif
436	if ((ntfs_log.flags & NTFS_LOG_FLAG_ONLYNAME) &&
437	    (strchr(file, PATH_SEP)))		/* Abbreviate the filename */
438		file = strrchr(file, PATH_SEP) + 1;
439
440	if (ntfs_log.flags & NTFS_LOG_FLAG_PREFIX)	/* Prefix the output */
441		ret += fprintf(stream, "%s", ntfs_log_get_prefix(level));
442
443	if (ntfs_log.flags & NTFS_LOG_FLAG_FILENAME)	/* Source filename */
444		ret += fprintf(stream, "%s ", file);
445
446	if (ntfs_log.flags & NTFS_LOG_FLAG_LINE)	/* Source line number */
447		ret += fprintf(stream, "(%d) ", line);
448
449	if ((ntfs_log.flags & NTFS_LOG_FLAG_FUNCTION) || /* Source function */
450	    (level & NTFS_LOG_LEVEL_TRACE) || (level & NTFS_LOG_LEVEL_ENTER))
451		ret += fprintf(stream, "%s(): ", function);
452
453	ret += vfprintf(stream, format, args);
454
455	if (level & NTFS_LOG_LEVEL_PERROR)
456		ret += fprintf(stream, ": %s\n", strerror(olderr));
457
458#ifdef DEBUG
459	if (level == NTFS_LOG_LEVEL_ENTER)
460		tab++;
461#endif
462	fflush(stream);
463	errno = olderr;
464	return ret;
465}
466
467/**
468 * ntfs_log_handler_null - Null logging handler (no output)
469 * @function:	Function in which the log line occurred
470 * @file:	File in which the log line occurred
471 * @line:	Line number on which the log line occurred
472 * @level:	Level at which the line is logged
473 * @data:	User specified data, possibly specific to a handler
474 * @format:	printf-style formatting string
475 * @args:	Arguments to be formatted
476 *
477 * This handler produces no output.  It provides a way to temporarily disable
478 * logging, without having to change the levels and flags.
479 *
480 * Returns:  0  Message wasn't logged
481 */
482int ntfs_log_handler_null(const char *function __attribute__((unused)), const char *file __attribute__((unused)),
483	int line __attribute__((unused)), u32 level __attribute__((unused)), void *data __attribute__((unused)),
484	const char *format __attribute__((unused)), va_list args __attribute__((unused)))
485{
486	return 0;
487}
488
489/**
490 * ntfs_log_handler_stdout - All logs go to stdout
491 * @function:	Function in which the log line occurred
492 * @file:	File in which the log line occurred
493 * @line:	Line number on which the log line occurred
494 * @level:	Level at which the line is logged
495 * @data:	User specified data, possibly specific to a handler
496 * @format:	printf-style formatting string
497 * @args:	Arguments to be formatted
498 *
499 * Display a log message to stdout.
500 *
501 * Note: For this handler, @data is a pointer to a FILE output stream.
502 *       If @data is NULL, then stdout will be used.
503 *
504 * Note: This function calls ntfs_log_handler_fprintf to do the main work.
505 *
506 * Returns:  -1  Error occurred
507 *            0  Message wasn't logged
508 *          num  Number of output characters
509 */
510int ntfs_log_handler_stdout(const char *function, const char *file,
511	int line, u32 level, void *data, const char *format, va_list args)
512{
513	if (!data)
514		data = stdout;
515
516	return ntfs_log_handler_fprintf(function, file, line, level, data, format, args);
517}
518
519/**
520 * ntfs_log_handler_outerr - Logs go to stdout/stderr depending on level
521 * @function:	Function in which the log line occurred
522 * @file:	File in which the log line occurred
523 * @line:	Line number on which the log line occurred
524 * @level:	Level at which the line is logged
525 * @data:	User specified data, possibly specific to a handler
526 * @format:	printf-style formatting string
527 * @args:	Arguments to be formatted
528 *
529 * Display a log message.  The output stream will be determined by the log
530 * level.
531 *
532 * Note: For this handler, @data is a pointer to a FILE output stream.
533 *       If @data is NULL, the function ntfs_log_get_stream will be called
534 *
535 * Note: This function calls ntfs_log_handler_fprintf to do the main work.
536 *
537 * Returns:  -1  Error occurred
538 *            0  Message wasn't logged
539 *          num  Number of output characters
540 */
541int ntfs_log_handler_outerr(const char *function, const char *file,
542	int line, u32 level, void *data, const char *format, va_list args)
543{
544	if (!data)
545		data = ntfs_log_get_stream(level);
546
547	return ntfs_log_handler_fprintf(function, file, line, level, data, format, args);
548}
549
550/**
551 * ntfs_log_handler_stderr - All logs go to stderr
552 * @function:	Function in which the log line occurred
553 * @file:	File in which the log line occurred
554 * @line:	Line number on which the log line occurred
555 * @level:	Level at which the line is logged
556 * @data:	User specified data, possibly specific to a handler
557 * @format:	printf-style formatting string
558 * @args:	Arguments to be formatted
559 *
560 * Display a log message to stderr.
561 *
562 * Note: For this handler, @data is a pointer to a FILE output stream.
563 *       If @data is NULL, then stdout will be used.
564 *
565 * Note: This function calls ntfs_log_handler_fprintf to do the main work.
566 *
567 * Returns:  -1  Error occurred
568 *            0  Message wasn't logged
569 *          num  Number of output characters
570 */
571int ntfs_log_handler_stderr(const char *function, const char *file,
572	int line, u32 level, void *data, const char *format, va_list args)
573{
574	if (!data)
575		data = stderr;
576
577	return ntfs_log_handler_fprintf(function, file, line, level, data, format, args);
578}
579
580
581/**
582 * ntfs_log_parse_option - Act upon command line options
583 * @option:	Option flag
584 *
585 * Delegate some of the work of parsing the command line.  All the options begin
586 * with "--log-".  Options cause log levels to be enabled in @ntfs_log (the
587 * global logging structure).
588 *
589 * Note: The "colour" option changes the logging handler.
590 *
591 * Returns:  TRUE  Option understood
592 *          FALSE  Invalid log option
593 */
594BOOL ntfs_log_parse_option(const char *option)
595{
596	if (strcmp(option, "--log-debug") == 0) {
597		ntfs_log_set_levels(NTFS_LOG_LEVEL_DEBUG);
598		return TRUE;
599	} else if (strcmp(option, "--log-verbose") == 0) {
600		ntfs_log_set_levels(NTFS_LOG_LEVEL_VERBOSE);
601		return TRUE;
602	} else if (strcmp(option, "--log-quiet") == 0) {
603		ntfs_log_clear_levels(NTFS_LOG_LEVEL_QUIET);
604		return TRUE;
605	} else if (strcmp(option, "--log-trace") == 0) {
606		ntfs_log_set_levels(NTFS_LOG_LEVEL_TRACE);
607		return TRUE;
608	}
609
610	ntfs_log_debug("Unknown logging option '%s'\n", option);
611	return FALSE;
612}
613
614