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