summaryrefslogtreecommitdiff
path: root/libntfs-3g/logging.c (plain)
blob: 385bcaa6ec020a30bc6c1b01855d0f87fedf84f9
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