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#ifndef __HAIKU__
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 */
201static FILE * ntfs_log_get_stream(u32 level)
202{
203	FILE *stream;
204
205	switch (level) {
206		case NTFS_LOG_LEVEL_INFO:
207		case NTFS_LOG_LEVEL_QUIET:
208		case NTFS_LOG_LEVEL_PROGRESS:
209		case NTFS_LOG_LEVEL_VERBOSE:
210			stream = stdout;
211			break;
212
213		case NTFS_LOG_LEVEL_DEBUG:
214		case NTFS_LOG_LEVEL_TRACE:
215		case NTFS_LOG_LEVEL_ENTER:
216		case NTFS_LOG_LEVEL_LEAVE:
217		case NTFS_LOG_LEVEL_WARNING:
218		case NTFS_LOG_LEVEL_ERROR:
219		case NTFS_LOG_LEVEL_CRITICAL:
220		case NTFS_LOG_LEVEL_PERROR:
221		default:
222			stream = stderr;
223			break;
224	}
225
226	return stream;
227}
228
229/**
230 * ntfs_log_get_prefix - Default prefixes for logging levels
231 * @level:	Log level to be prefixed
232 *
233 * Prefixing the logging output can make it easier to parse.
234 *
235 * Returns:  "string"  Prefix to be used
236 */
237static const char * ntfs_log_get_prefix(u32 level)
238{
239	const char *prefix;
240
241	switch (level) {
242		case NTFS_LOG_LEVEL_DEBUG:
243			prefix = "DEBUG: ";
244			break;
245		case NTFS_LOG_LEVEL_TRACE:
246			prefix = "TRACE: ";
247			break;
248		case NTFS_LOG_LEVEL_QUIET:
249			prefix = "QUIET: ";
250			break;
251		case NTFS_LOG_LEVEL_INFO:
252			prefix = "INFO: ";
253			break;
254		case NTFS_LOG_LEVEL_VERBOSE:
255			prefix = "VERBOSE: ";
256			break;
257		case NTFS_LOG_LEVEL_PROGRESS:
258			prefix = "PROGRESS: ";
259			break;
260		case NTFS_LOG_LEVEL_WARNING:
261			prefix = "WARNING: ";
262			break;
263		case NTFS_LOG_LEVEL_ERROR:
264			prefix = "ERROR: ";
265			break;
266		case NTFS_LOG_LEVEL_PERROR:
267			prefix = "ERROR: ";
268			break;
269		case NTFS_LOG_LEVEL_CRITICAL:
270			prefix = "CRITICAL: ";
271			break;
272		default:
273			prefix = "";
274			break;
275	}
276
277	return prefix;
278}
279#endif
280
281
282/**
283 * ntfs_log_set_handler - Provide an alternate logging handler
284 * @handler:	function to perform the logging
285 *
286 * This alternate handler will be called for all future logging requests.
287 * If no @handler is specified, logging will revert to the default handler.
288 */
289void ntfs_log_set_handler(ntfs_log_handler *handler)
290{
291	if (handler) {
292		ntfs_log.handler = handler;
293#ifdef HAVE_SYSLOG_H
294		if (handler == ntfs_log_handler_syslog)
295			openlog("ntfs-3g", LOG_PID, LOG_USER);
296#endif
297	} else
298		ntfs_log.handler = ntfs_log_handler_null;
299}
300
301/**
302 * ntfs_log_redirect - Pass on the request to the real handler
303 * @function:	Function in which the log line occurred
304 * @file:	File in which the log line occurred
305 * @line:	Line number on which the log line occurred
306 * @level:	Level at which the line is logged
307 * @data:	User specified data, possibly specific to a handler
308 * @format:	printf-style formatting string
309 * @...:	Arguments to be formatted
310 *
311 * This is just a redirector function.  The arguments are simply passed to the
312 * main logging handler (as defined in the global logging struct @ntfs_log).
313 *
314 * Returns:  -1  Error occurred
315 *            0  Message wasn't logged
316 *          num  Number of output characters
317 */
318int ntfs_log_redirect(const char *function, const char *file,
319	int line, u32 level, void *data, const char *format, ...)
320{
321	int olderr = errno;
322	int ret;
323	va_list args;
324
325	if (!(ntfs_log.levels & level))		/* Don't log this message */
326		return 0;
327
328	va_start(args, format);
329	errno = olderr;
330	ret = ntfs_log.handler(function, file, line, level, data, format, args);
331	va_end(args);
332
333	errno = olderr;
334	return ret;
335}
336
337
338/**
339 * ntfs_log_handler_syslog - syslog logging handler
340 * @function:	Function in which the log line occurred
341 * @file:	File in which the log line occurred
342 * @line:	Line number on which the log line occurred
343 * @level:	Level at which the line is logged
344 * @data:	User specified data, possibly specific to a handler
345 * @format:	printf-style formatting string
346 * @args:	Arguments to be formatted
347 *
348 * A simple syslog logging handler.  Ignores colors.
349 *
350 * Returns:  -1  Error occurred
351 *            0  Message wasn't logged
352 *          num  Number of output characters
353 */
354
355
356#ifdef HAVE_SYSLOG_H
357
358#define LOG_LINE_LEN 	512
359
360int ntfs_log_handler_syslog(const char *function  __attribute__((unused)),
361			    const char *file __attribute__((unused)),
362			    int line __attribute__((unused)), u32 level,
363			    void *data __attribute__((unused)),
364			    const char *format, va_list args)
365{
366	char logbuf[LOG_LINE_LEN];
367	int ret, olderr = errno;
368
369#ifndef DEBUG
370	if ((level & NTFS_LOG_LEVEL_PERROR) && errno == ENOSPC)
371		return 1;
372#endif
373	ret = vsnprintf(logbuf, LOG_LINE_LEN, format, args);
374	if (ret < 0) {
375		vsyslog(LOG_NOTICE, format, args);
376		ret = 1;
377		goto out;
378	}
379
380	if ((LOG_LINE_LEN > ret + 3) && (level & NTFS_LOG_LEVEL_PERROR)) {
381		strncat(logbuf, ": ", LOG_LINE_LEN - ret - 1);
382		strncat(logbuf, strerror(olderr), LOG_LINE_LEN - (ret + 3));
383		ret = strlen(logbuf);
384	}
385
386	syslog(LOG_NOTICE, "%s", logbuf);
387out:
388	errno = olderr;
389	return ret;
390}
391#endif
392
393/*
394 *			Early logging before the logs are redirected
395 *
396 *	(not quite satisfactory : this appears before the ntfs-g banner,
397 *	and with a different pid)
398 */
399
400void ntfs_log_early_error(const char *format, ...)
401{
402#ifndef __HAIKU__
403	va_list args;
404
405	va_start(args, format);
406#ifdef HAVE_SYSLOG_H
407	openlog("ntfs-3g", LOG_PID, LOG_USER);
408	ntfs_log_handler_syslog(NULL, NULL, 0,
409		NTFS_LOG_LEVEL_ERROR, NULL,
410		format, args);
411#else
412	vfprintf(stderr,format,args);
413#endif
414	va_end(args);
415#endif
416}
417
418#ifndef __HAIKU__
419/**
420 * ntfs_log_handler_fprintf - Basic logging handler
421 * @function:	Function in which the log line occurred
422 * @file:	File in which the log line occurred
423 * @line:	Line number on which the log line occurred
424 * @level:	Level at which the line is logged
425 * @data:	User specified data, possibly specific to a handler
426 * @format:	printf-style formatting string
427 * @args:	Arguments to be formatted
428 *
429 * A simple logging handler.  This is where the log line is finally displayed.
430 * It is more likely that you will want to set the handler to either
431 * ntfs_log_handler_outerr or ntfs_log_handler_stderr.
432 *
433 * Note: For this handler, @data is a pointer to a FILE output stream.
434 *       If @data is NULL, nothing will be displayed.
435 *
436 * Returns:  -1  Error occurred
437 *            0  Message wasn't logged
438 *          num  Number of output characters
439 */
440int ntfs_log_handler_fprintf(const char *function, const char *file,
441	int line, u32 level, void *data, const char *format, va_list args)
442{
443#ifdef DEBUG
444	int i;
445#endif
446	int ret = 0;
447	int olderr = errno;
448	FILE *stream;
449
450	if (!data)		/* Interpret data as a FILE stream. */
451		return 0;	/* If it's NULL, we can't do anything. */
452	stream = (FILE*)data;
453
454#ifdef DEBUG
455	if (level == NTFS_LOG_LEVEL_LEAVE) {
456		if (tab)
457			tab--;
458		return 0;
459	}
460
461	for (i = 0; i < tab; i++)
462		ret += fprintf(stream, " ");
463#endif
464	if ((ntfs_log.flags & NTFS_LOG_FLAG_ONLYNAME) &&
465	    (strchr(file, PATH_SEP)))		/* Abbreviate the filename */
466		file = strrchr(file, PATH_SEP) + 1;
467
468	if (ntfs_log.flags & NTFS_LOG_FLAG_PREFIX)	/* Prefix the output */
469		ret += fprintf(stream, "%s", ntfs_log_get_prefix(level));
470
471	if (ntfs_log.flags & NTFS_LOG_FLAG_FILENAME)	/* Source filename */
472		ret += fprintf(stream, "%s ", file);
473
474	if (ntfs_log.flags & NTFS_LOG_FLAG_LINE)	/* Source line number */
475		ret += fprintf(stream, "(%d) ", line);
476
477	if ((ntfs_log.flags & NTFS_LOG_FLAG_FUNCTION) || /* Source function */
478	    (level & NTFS_LOG_LEVEL_TRACE) || (level & NTFS_LOG_LEVEL_ENTER))
479		ret += fprintf(stream, "%s(): ", function);
480
481	ret += vfprintf(stream, format, args);
482
483	if (level & NTFS_LOG_LEVEL_PERROR)
484		ret += fprintf(stream, ": %s\n", strerror(olderr));
485
486#ifdef DEBUG
487	if (level == NTFS_LOG_LEVEL_ENTER)
488		tab++;
489#endif
490	fflush(stream);
491	errno = olderr;
492	return ret;
493}
494
495#endif // __HAIKU__
496
497/**
498 * ntfs_log_handler_null - Null logging handler (no output)
499 * @function:	Function in which the log line occurred
500 * @file:	File in which the log line occurred
501 * @line:	Line number on which the log line occurred
502 * @level:	Level at which the line is logged
503 * @data:	User specified data, possibly specific to a handler
504 * @format:	printf-style formatting string
505 * @args:	Arguments to be formatted
506 *
507 * This handler produces no output.  It provides a way to temporarily disable
508 * logging, without having to change the levels and flags.
509 *
510 * Returns:  0  Message wasn't logged
511 */
512int ntfs_log_handler_null(const char *function __attribute__((unused)), const char *file __attribute__((unused)),
513	int line __attribute__((unused)), u32 level __attribute__((unused)), void *data __attribute__((unused)),
514	const char *format __attribute__((unused)), va_list args __attribute__((unused)))
515{
516	return 0;
517}
518
519#ifndef __HAIKU__
520
521/**
522 * ntfs_log_handler_stdout - All logs go to stdout
523 * @function:	Function in which the log line occurred
524 * @file:	File in which the log line occurred
525 * @line:	Line number on which the log line occurred
526 * @level:	Level at which the line is logged
527 * @data:	User specified data, possibly specific to a handler
528 * @format:	printf-style formatting string
529 * @args:	Arguments to be formatted
530 *
531 * Display a log message to stdout.
532 *
533 * Note: For this handler, @data is a pointer to a FILE output stream.
534 *       If @data is NULL, then stdout will be used.
535 *
536 * Note: This function calls ntfs_log_handler_fprintf to do the main work.
537 *
538 * Returns:  -1  Error occurred
539 *            0  Message wasn't logged
540 *          num  Number of output characters
541 */
542int ntfs_log_handler_stdout(const char *function, const char *file,
543	int line, u32 level, void *data, const char *format, va_list args)
544{
545	if (!data)
546		data = stdout;
547
548	return ntfs_log_handler_fprintf(function, file, line, level, data, format, args);
549}
550
551/**
552 * ntfs_log_handler_outerr - Logs go to stdout/stderr depending on level
553 * @function:	Function in which the log line occurred
554 * @file:	File in which the log line occurred
555 * @line:	Line number on which the log line occurred
556 * @level:	Level at which the line is logged
557 * @data:	User specified data, possibly specific to a handler
558 * @format:	printf-style formatting string
559 * @args:	Arguments to be formatted
560 *
561 * Display a log message.  The output stream will be determined by the log
562 * level.
563 *
564 * Note: For this handler, @data is a pointer to a FILE output stream.
565 *       If @data is NULL, the function ntfs_log_get_stream will be called
566 *
567 * Note: This function calls ntfs_log_handler_fprintf to do the main work.
568 *
569 * Returns:  -1  Error occurred
570 *            0  Message wasn't logged
571 *          num  Number of output characters
572 */
573int ntfs_log_handler_outerr(const char *function, const char *file,
574	int line, u32 level, void *data, const char *format, va_list args)
575{
576	if (!data)
577		data = ntfs_log_get_stream(level);
578
579	return ntfs_log_handler_fprintf(function, file, line, level, data, format, args);
580}
581
582/**
583 * ntfs_log_handler_stderr - All logs go to stderr
584 * @function:	Function in which the log line occurred
585 * @file:	File in which the log line occurred
586 * @line:	Line number on which the log line occurred
587 * @level:	Level at which the line is logged
588 * @data:	User specified data, possibly specific to a handler
589 * @format:	printf-style formatting string
590 * @args:	Arguments to be formatted
591 *
592 * Display a log message to stderr.
593 *
594 * Note: For this handler, @data is a pointer to a FILE output stream.
595 *       If @data is NULL, then stdout will be used.
596 *
597 * Note: This function calls ntfs_log_handler_fprintf to do the main work.
598 *
599 * Returns:  -1  Error occurred
600 *            0  Message wasn't logged
601 *          num  Number of output characters
602 */
603int ntfs_log_handler_stderr(const char *function, const char *file,
604	int line, u32 level, void *data, const char *format, va_list args)
605{
606	if (!data)
607		data = stderr;
608
609	return ntfs_log_handler_fprintf(function, file, line, level, data, format, args);
610}
611
612#endif // __HAIKU__
613
614/**
615 * ntfs_log_parse_option - Act upon command line options
616 * @option:	Option flag
617 *
618 * Delegate some of the work of parsing the command line.  All the options begin
619 * with "--log-".  Options cause log levels to be enabled in @ntfs_log (the
620 * global logging structure).
621 *
622 * Note: The "colour" option changes the logging handler.
623 *
624 * Returns:  TRUE  Option understood
625 *          FALSE  Invalid log option
626 */
627BOOL ntfs_log_parse_option(const char *option)
628{
629	if (strcmp(option, "--log-debug") == 0) {
630		ntfs_log_set_levels(NTFS_LOG_LEVEL_DEBUG);
631		return TRUE;
632	} else if (strcmp(option, "--log-verbose") == 0) {
633		ntfs_log_set_levels(NTFS_LOG_LEVEL_VERBOSE);
634		return TRUE;
635	} else if (strcmp(option, "--log-quiet") == 0) {
636		ntfs_log_clear_levels(NTFS_LOG_LEVEL_QUIET);
637		return TRUE;
638	} else if (strcmp(option, "--log-trace") == 0) {
639		ntfs_log_set_levels(NTFS_LOG_LEVEL_TRACE);
640		return TRUE;
641	}
642
643	ntfs_log_debug("Unknown logging option '%s'\n", option);
644	return FALSE;
645}
646
647